Index: 3rdParty_sources/jbossweb/org/jboss/logging/DynamicLogger.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/logging/DynamicLogger.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/logging/DynamicLogger.java 17 Aug 2012 14:43:38 -0000 1.1 @@ -0,0 +1,265 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2005, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jboss.logging; + +/** + * An extension of the JBoss Logger that adds a log() + * primitive that maps to a dynamically defined log level. + * + * TODO - Make sure serialization works correctly + * + * @author Dimitris Andreadis + * @version $Revision: 1.1 $ + * + * @since 4.0.3 + */ +public class DynamicLogger extends Logger +{ + /** The serialVersionUID */ + private static final long serialVersionUID = -5963699806863917370L; + + /** No logging */ + public static final int LOG_LEVEL_NONE = 0; + + /** Fatal level logging */ + public static final int LOG_LEVEL_FATAL = 1; + + /** Error level logging */ + public static final int LOG_LEVEL_ERROR = 2; + + /** Warn level logging */ + public static final int LOG_LEVEL_WARN = 3; + + /** Info level logging */ + public static final int LOG_LEVEL_INFO = 4; + + /** Debug level logging */ + public static final int LOG_LEVEL_DEBUG = 5; + + /** Trace level logging */ + public static final int LOG_LEVEL_TRACE = 6; + + /** The available log level strings */ + public final static String[] LOG_LEVEL_STRINGS = + { "NONE", "FATAL", "ERROR", "WARN", "INFO", "DEBUG", "TRACE" }; + + /** The log level to use for the "log" primitive */ + private int logLevel = LOG_LEVEL_DEBUG; + + /** + * Create a DynamicLogger instance given the logger name. + * + * @param name the logger name + * @return the dynamic logger + */ + public static DynamicLogger getDynamicLogger(String name) + { + return new DynamicLogger(name); + } + + /** + * Create a DynamicLogger instance given the logger name with the given suffix. + * + *

This will include a logger seperator between classname and suffix + * + * @param name The logger name + * @param suffix A suffix to append to the classname. + * @return the dynamic logger + */ + public static DynamicLogger getDynamicLogger(String name, String suffix) + { + return new DynamicLogger(name + "." + suffix); + } + + /** + * Create a DynamicLogger instance given the logger class. This simply + * calls create(clazz.getName()). + * + * @param clazz the Class whose name will be used as the logger name + * @return the dynamic logger + */ + public static DynamicLogger getDynamicLogger(Class clazz) + { + return new DynamicLogger(clazz.getName()); + } + + /** + * Create a DynamicLogger instance given the logger class with the given suffix. + * + *

This will include a logger seperator between classname and suffix + * + * @param clazz The Class whose name will be used as the logger name. + * @param suffix A suffix to append to the classname. + * @return the dynamic logger + */ + public static DynamicLogger getDynamicLogger(Class clazz, String suffix) + { + return new DynamicLogger(clazz.getName() + "." + suffix); + } + + /** + * Create a new DynamicLogger. + * + * @param name the log name + */ + protected DynamicLogger(final String name) + { + super(name); + } + + /** + * Sets the logLevel for the log() primitive + * + * @param logLevel between LOG_LEVEL_NONE and LOG_LEVEL_TRACE + */ + public void setLogLevel(int logLevel) + { + if (logLevel >= LOG_LEVEL_NONE && logLevel <= LOG_LEVEL_TRACE) + { + this.logLevel = logLevel; + } + } + + /** + * Gets the logLevel of the log() primitive + * + * @return the logLevel of the log() primitive + */ + public int getLogLevel() + { + return logLevel; + } + + /** + * Sets the logLevel of the log() primitive + * + * @param logLevelString the log level in String form + */ + public void setLogLevelAsString(String logLevelString) + { + if (logLevelString != null) + { + logLevelString = logLevelString.toUpperCase().trim(); + + for (int i = 0; i <= LOG_LEVEL_TRACE; i++) + { + if (logLevelString.equals(LOG_LEVEL_STRINGS[i])) + { + // match + this.logLevel = i; + break; + } + } + } + } + + /** + * Gets the logLevel of the log() primitive in String form + * + * @return the logLevel of the log() primitive in String form + */ + public String getLogLevelAsString() + { + return LOG_LEVEL_STRINGS[logLevel]; + } + + /** + * Logs a message using dynamic log level + * + * @param message the message to log + */ + public void log(Object message) + { + switch (logLevel) + { + case LOG_LEVEL_TRACE: + super.trace(message); + break; + + case LOG_LEVEL_DEBUG: + super.debug(message); + break; + + case LOG_LEVEL_INFO: + super.info(message); + break; + + case LOG_LEVEL_WARN: + super.warn(message); + break; + + case LOG_LEVEL_ERROR: + super.error(message); + break; + + case LOG_LEVEL_FATAL: + super.fatal(message); + break; + + case LOG_LEVEL_NONE: + default: + // do nothing + break; + } + } + + /** + * Logs a message and a throwable using dynamic log level + * + * @param message the message to log + * @param t the throwable to log + */ + public void log(Object message, Throwable t) + { + switch (logLevel) + { + case LOG_LEVEL_TRACE: + super.trace(message, t); + break; + + case LOG_LEVEL_DEBUG: + super.debug(message, t); + break; + + case LOG_LEVEL_INFO: + super.info(message, t); + break; + + case LOG_LEVEL_WARN: + super.warn(message, t); + break; + + case LOG_LEVEL_ERROR: + super.error(message, t); + break; + + case LOG_LEVEL_FATAL: + super.fatal(message, t); + break; + + case LOG_LEVEL_NONE: + default: + // do nothing + break; + } + } +} Index: 3rdParty_sources/jbossweb/org/jboss/logging/Logger.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/logging/Logger.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/logging/Logger.java 17 Aug 2012 14:43:38 -0000 1.1 @@ -0,0 +1,434 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2005, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jboss.logging; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.Serializable; + +/** + * Logger wrapper that tries to dynamically load a log4j class to + * determine if log4j is available in the VM. If it is the case, + * a log4j delegate is built and used. In the contrary, a null + * logger is used. This class cannot directly reference log4j + * classes otherwise the JVM will try to load it and make it fail. + * To set + * + *

Only exposes the relevent factory and logging methods. + * + *

For JBoss the logging should be done as follows: + *

+ * + * @see #isTraceEnabled + * @see #trace(Object) + * @see #trace(Object,Throwable) + * + * @version $Revision: 1.1 $ + * @author Scott.Stark@jboss.org + * @author Jason Dillon + * @author Sacha Labourey + */ +public class Logger implements Serializable +{ + /** Serialization */ + private static final long serialVersionUID = 4232175575988879434L; + + /** The system property to look for an externalized LoggerPlugin implementation class */ + protected static String PLUGIN_CLASS_PROP = "org.jboss.logging.Logger.pluginClass"; + + /** The default LoggerPlugin implementation is log4j */ + protected static final String DEFAULT_PLUGIN_CLASS_NAME = "org.jboss.logging.jdk.JDK14LoggerPlugin"; + + /** The LoggerPlugin implementation class to use */ + protected static Class pluginClass = null; + + /** The class name of the LoggerPlugin implementation class to use */ + protected static String pluginClassName = null; + + static + { + init(); + } + + /** The logger name. */ + private final String name; + + /** The logger plugin delegate */ + protected transient LoggerPlugin loggerDelegate = null; + + /** The LoggerPlugin implementation class name in use + * + * @return LoggerPlugin implementation class name + */ + public static String getPluginClassName() + { + return Logger.pluginClassName; + } + + /** + * Set the LoggerPlugin implementation class name in use + * + * @param pluginClassName the LoggerPlugin implementation class name + */ + public static void setPluginClassName(String pluginClassName) + { + if (pluginClassName.equals(Logger.pluginClassName) == false) + { + Logger.pluginClassName = pluginClassName; + init(); + } + } + + /** + * Creates new Logger the given logger name. + * + * @param name the logger name. + */ + protected Logger(final String name) + { + this.name = name; + this.loggerDelegate = getDelegatePlugin(name); + } + + /** + * Return the name of this logger. + * + * @return The name of this logger. + */ + public String getName() + { + return name; + } + + /** + * Get the logger plugin delegate + * + * @return the delegate + */ + public LoggerPlugin getLoggerPlugin() + { + return this.loggerDelegate; + } + + /** + * Check to see if the TRACE level is enabled for this logger. + * + * @return true if a {@link #trace(Object)} method invocation would pass + * the msg to the configured appenders, false otherwise. + */ + public boolean isTraceEnabled() + { + return loggerDelegate.isTraceEnabled(); + } + + /** + * Issue a log msg with a level of TRACE. + * + * @param message the message + */ + public void trace(Object message) + { + loggerDelegate.trace(message); + } + + /** + * Issue a log msg and throwable with a level of TRACE. + * + * @param message the message + * @param t the throwable + */ + public void trace(Object message, Throwable t) + { + loggerDelegate.trace(message, t); + } + + /** + * Check to see if the DEBUG level is enabled for this logger. + * + * @deprecated DEBUG is for low volume logging, you don't need this + * @return true if a {@link #trace(Object)} method invocation would pass + * the msg to the configured appenders, false otherwise. + */ + public boolean isDebugEnabled() + { + return loggerDelegate.isDebugEnabled(); + } + + /** + * Issue a log msg with a level of DEBUG. + * + * @param message the message + */ + public void debug(Object message) + { + loggerDelegate.debug(message); + } + + /** + * Issue a log msg and throwable with a level of DEBUG. + * + * @param message the message + * @param t the throwable + */ + public void debug(Object message, Throwable t) + { + loggerDelegate.debug(message, t); + } + + /** + * Check to see if the INFO level is enabled for this logger. + * + * @deprecated INFO is for low volume information, you don't need this + * @return true if a {@link #info(Object)} method invocation would pass + * the msg to the configured appenders, false otherwise. + */ + public boolean isInfoEnabled() + { + return loggerDelegate.isInfoEnabled(); + } + + /** + * Issue a log msg with a level of INFO. + * + * @param message the message + */ + public void info(Object message) + { + loggerDelegate.info(message); + } + + /** + * Issue a log msg and throwable with a level of INFO. + * + * @param message the message + * @param t the throwable + */ + public void info(Object message, Throwable t) + { + loggerDelegate.info(message, t); + } + + /** + * Issue a log msg with a level of WARN. + * + * @param message the message + */ + public void warn(Object message) + { + loggerDelegate.warn(message); + } + + /** + * Issue a log msg and throwable with a level of WARN. + * + * @param message the message + * @param t the throwable + */ + public void warn(Object message, Throwable t) + { + loggerDelegate.warn(message, t); + } + + /** + * Issue a log msg with a level of ERROR. + * + * @param message the message + */ + public void error(Object message) + { + loggerDelegate.error(message); + } + + /** + * Issue a log msg and throwable with a level of ERROR. + * + * @param message the message + * @param t the throwable + */ + public void error(Object message, Throwable t) + { + loggerDelegate.error(message, t); + } + + /** + * Issue a log msg with a level of FATAL. + * + * @param message the message + */ + public void fatal(Object message) + { + loggerDelegate.fatal(message); + } + + /** + * Issue a log msg and throwable with a level of FATAL. + * + * @param message the message + * @param t the throwable + */ + public void fatal(Object message, Throwable t) + { + loggerDelegate.fatal(message, t); + } + + /** + * Custom serialization to reinitalize the delegate + * + * @param stream the object stream + * @throws IOException for any error + * @throws ClassNotFoundException if a class is not found during deserialization + */ + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException + { + // restore non-transient fields (aka name) + stream.defaultReadObject(); + + // Restore logging + if (pluginClass == null) + { + init(); + } + this.loggerDelegate = getDelegatePlugin(name); + } + + /** + * Create a Logger instance given the logger name. + * + * @param name the logger name + * @return the logger + */ + public static Logger getLogger(String name) + { + return new Logger(name); + } + + /** + * Create a Logger instance given the logger name with the given suffix. + * + *

This will include a logger seperator between classname and suffix + * + * @param name the logger name + * @param suffix a suffix to append to the classname. + * @return the logger + */ + public static Logger getLogger(String name, String suffix) + { + return new Logger(name + "." + suffix); + } + + /** + * Create a Logger instance given the logger class. This simply + * calls create(clazz.getName()). + * + * @param clazz the Class whose name will be used as the logger name + * @return the logger + */ + public static Logger getLogger(Class clazz) + { + return new Logger(clazz.getName()); + } + + /** + * Create a Logger instance given the logger class with the given suffix. + * + *

This will include a logger seperator between classname and suffix + * + * @param clazz the Class whose name will be used as the logger name. + * @param suffix a suffix to append to the classname. + * @return the logger + */ + public static Logger getLogger(Class clazz, String suffix) + { + return new Logger(clazz.getName() + "." + suffix); + } + + /** + * Get the delegate plugin + * + * @param name the name of the logger + * @return the plugin + */ + protected static LoggerPlugin getDelegatePlugin(String name) + { + LoggerPlugin plugin = null; + try + { + plugin = (LoggerPlugin) pluginClass.newInstance(); + } + catch (Throwable e) + { + plugin = new NullLoggerPlugin(); + } + try + { + plugin.init(name); + } + catch (Throwable e) + { + String extraInfo = e.getMessage(); + System.err.println("Failed to initalize plugin: " + plugin + + (extraInfo != null ? ", cause: " + extraInfo : "")); + plugin = new NullLoggerPlugin(); + } + + return plugin; + } + + /** + * Initialize the LoggerPlugin class to use as the delegate to the + * logging system. This first checks to see if a pluginClassName has + * been specified via the {@link #setPluginClassName(String)} method, + * then the PLUGIN_CLASS_PROP system property and finally the + * LOG4J_PLUGIN_CLASS_NAME default. If the LoggerPlugin implementation + * class cannot be loaded the default NullLoggerPlugin will be used. + */ + protected static void init() + { + try + { + // See if there is a PLUGIN_CLASS_PROP specified + if (pluginClassName == null) + { + pluginClassName = System.getProperty(PLUGIN_CLASS_PROP, DEFAULT_PLUGIN_CLASS_NAME); + } + + // Try to load the plugin via the TCL + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + pluginClass = cl.loadClass(pluginClassName); + } + catch (Throwable e) + { + // The plugin could not be setup, default to a null logger + pluginClass = org.jboss.logging.NullLoggerPlugin.class; + } + } +} Index: 3rdParty_sources/jbossweb/org/jboss/logging/LoggerPlugin.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/logging/LoggerPlugin.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/logging/LoggerPlugin.java 17 Aug 2012 14:43:38 -0000 1.1 @@ -0,0 +1,158 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2005, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jboss.logging; + +/** + * Defines a "pluggable" login module. In fact, this is only used to split between + * log4j and /dev/null. Choice is made in org.jboss.logging.Logger + * + * @see org.jboss.logging.Logger + * @see org.jboss.logging.NullLoggerPlugin + * + * @author Sacha Labourey. + * @version $Revision: 1.1 $ + */ +public interface LoggerPlugin +{ + /** + * Initialise the logger with the given name + * + * @param name the name + */ + void init(String name); + + /** + * Check to see if the TRACE level is enabled for this logger. + * + * @return true if a {@link #trace(Object)} method invocation would pass + * the msg to the configured appenders, false otherwise. + */ + boolean isTraceEnabled(); + + /** + * Issue a log msg with a level of TRACE. + * + * @param message the message + */ + void trace(Object message); + + /** + * Issue a log msg and throwable with a level of TRACE. + * + * @param message the message + * @param t the throwable + */ + void trace(Object message, Throwable t); + + /** + * Check to see if the DEBUG level is enabled for this logger. + * + * @deprecated DEBUG is for low volume logging, you don't need this + * @return true if a {@link #trace(Object)} method invocation would pass + * the msg to the configured appenders, false otherwise. + */ + boolean isDebugEnabled(); + + /** + * Issue a log msg with a level of DEBUG. + * + * @param message the message + */ + void debug(Object message); + + /** + * Issue a log msg and throwable with a level of DEBUG. + * + * @param message the message + * @param t the throwable + */ + void debug(Object message, Throwable t); + + /** + * Check to see if the INFO level is enabled for this logger. + * + * @deprecated INFO is for low volume information, you don't need this + * @return true if a {@link #info(Object)} method invocation would pass + * the msg to the configured appenders, false otherwise. + */ + boolean isInfoEnabled(); + + /** + * Issue a log msg with a level of INFO. + * + * @param message the message + */ + void info(Object message); + + /** + * Issue a log msg and throwable with a level of INFO. + * + * @param message the message + * @param t the throwable + */ + void info(Object message, Throwable t); + + /** + * Issue a log msg with a level of WARN. + * + * @param message the message + */ + void warn(Object message); + + /** + * Issue a log msg and throwable with a level of WARN. + * + * @param message the message + * @param t the throwable + */ + void warn(Object message, Throwable t); + + /** + * Issue a log msg with a level of ERROR. + * + * @param message the message + */ + void error(Object message); + + /** + * Issue a log msg and throwable with a level of ERROR. + * + * @param message the message + * @param t the throwable + */ + void error(Object message, Throwable t); + + /** + * Issue a log msg with a level of FATAL. + * + * @param message the message + */ + void fatal(Object message); + + /** + * Issue a log msg and throwable with a level of FATAL. + * + * @param message the message + * @param t the throwable + */ + void fatal(Object message, Throwable t); +} Index: 3rdParty_sources/jbossweb/org/jboss/logging/MDC.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/logging/MDC.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/logging/MDC.java 17 Aug 2012 14:43:38 -0000 1.1 @@ -0,0 +1,76 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2005, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jboss.logging; + +import java.util.Map; + +/** + * A "Map Diagnostic Context" abstraction. + * + * @author Jason T. Greene + */ +public class MDC +{ + private final static MDCProvider mdc; + + static + { + MDCProvider m = null; + if (NDCSupport.class.isAssignableFrom(Logger.pluginClass)) + { + + try + { + m = ((MDCSupport) Logger.pluginClass.newInstance()).getMDCProvider(); + } + catch (Throwable t) + { + // Eat + } + } + + if (m == null) + m = new NullMDCProvider(); + + mdc = m; + } + + public static void put(String key, Object val) + { + mdc.put(key, val); + } + + public static Object get(String key) + { + return mdc.get(key); + } + + public static void remove(String key) + { + mdc.remove(key); + } + + public static Map getMap() + { + return mdc.getMap(); + } +} Index: 3rdParty_sources/jbossweb/org/jboss/logging/MDCProvider.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/logging/MDCProvider.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/logging/MDCProvider.java 17 Aug 2012 14:43:38 -0000 1.1 @@ -0,0 +1,40 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2005, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jboss.logging; + +import java.util.Map; + +/** + * MDC SPI for the backend logging implementation. + * + * @author Jason T. Greene + */ +public interface MDCProvider +{ + public void put(String key, Object value); + + public Object get(String key); + + public void remove(String key); + + public Map getMap(); +} Index: 3rdParty_sources/jbossweb/org/jboss/logging/MDCSupport.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/logging/MDCSupport.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/logging/MDCSupport.java 17 Aug 2012 14:43:38 -0000 1.1 @@ -0,0 +1,32 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2005, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jboss.logging; + +/** + * Indicates that a Logger plugin supports MDC. + * + * @author Jason T. Greene + */ +public interface MDCSupport +{ + public MDCProvider getMDCProvider(); +} Index: 3rdParty_sources/jbossweb/org/jboss/logging/NDC.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/logging/NDC.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/logging/NDC.java 17 Aug 2012 14:43:38 -0000 1.1 @@ -0,0 +1,90 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2005, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jboss.logging; + + +/** + * A "Nested Diagnostic Context" abstraction. + * + * @author Jason T. Greene + */ +public class NDC +{ + private final static NDCProvider ndc; + + static + { + NDCProvider n = null; + if (NDCSupport.class.isAssignableFrom(Logger.pluginClass)) + { + + try + { + n = ((NDCSupport) Logger.pluginClass.newInstance()).getNDCProvider(); + } + catch (Throwable t) + { + // Eat + } + } + + if (n == null) + n = new NullNDCProvider(); + + ndc = n; + } + + public static void clear() + { + ndc.clear(); + } + + public static String get() + { + return ndc.get(); + } + + public static int getDepth() + { + return ndc.getDepth(); + } + + public static String pop() + { + return ndc.pop(); + } + + public static String peek() + { + return ndc.peek(); + } + + public static void push(String message) + { + ndc.push(message); + } + + public static void setMaxDepth(int maxDepth) + { + ndc.setMaxDepth(maxDepth); + } +} Index: 3rdParty_sources/jbossweb/org/jboss/logging/NDCProvider.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/logging/NDCProvider.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/logging/NDCProvider.java 17 Aug 2012 14:43:38 -0000 1.1 @@ -0,0 +1,45 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2005, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jboss.logging; + + +/** + * An NDC SPI for the backend logging implementation. + * + * @author Jason T. Greene + */ +public interface NDCProvider +{ + public void clear(); + + public String get(); + + public int getDepth(); + + public String pop(); + + public String peek(); + + public void push(String message); + + public void setMaxDepth(int maxDepth); +} \ No newline at end of file Index: 3rdParty_sources/jbossweb/org/jboss/logging/NDCSupport.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/logging/NDCSupport.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/logging/NDCSupport.java 17 Aug 2012 14:43:38 -0000 1.1 @@ -0,0 +1,32 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2005, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jboss.logging; + +/** + * Indicates that a logger plugin supports NDC. + * + * @author Jason T. Greene + */ +public interface NDCSupport +{ + public NDCProvider getNDCProvider(); +} Index: 3rdParty_sources/jbossweb/org/jboss/logging/NullLoggerPlugin.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/logging/NullLoggerPlugin.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/logging/NullLoggerPlugin.java 17 Aug 2012 14:43:38 -0000 1.1 @@ -0,0 +1,115 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2005, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jboss.logging; + +/** + * LoggerPlugin implementation producing no output at all. Used for client + * side logging when no log4j.jar is available on the classpath. + * + * @see org.jboss.logging.Logger + * @see org.jboss.logging.LoggerPlugin + * + * @author Sacha Labourey. + * @version $Revision: 1.1 $ + */ +public class NullLoggerPlugin implements LoggerPlugin +{ + public void init(String name) + { + /* don't care */ + } + + public boolean isTraceEnabled() + { + return false; + } + + public void trace(Object message) + { + // nothing + } + + public void trace(Object message, Throwable t) + { + // nothing + } + + public boolean isDebugEnabled() + { + return false; + } + + public void debug(Object message) + { + // nothing + } + + public void debug(Object message, Throwable t) + { + // nothing + } + + public boolean isInfoEnabled() + { + return false; + } + + public void info(Object message) + { + // nothing + } + + public void info(Object message, Throwable t) + { + // nothing + } + + public void error(Object message) + { + // nothing + } + + public void error(Object message, Throwable t) + { + // nothing + } + + public void fatal(Object message) + { + // nothing + } + + public void fatal(Object message, Throwable t) + { + // nothing + } + + public void warn(Object message) + { + // nothing + } + + public void warn(Object message, Throwable t) + { + // nothing + } +} Index: 3rdParty_sources/jbossweb/org/jboss/logging/NullMDCProvider.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/logging/NullMDCProvider.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/logging/NullMDCProvider.java 17 Aug 2012 14:43:38 -0000 1.1 @@ -0,0 +1,50 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2005, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jboss.logging; + +import java.util.Map; + +/** + * An MDC provider which does nothing. + * + * @author Jason T. Greene + */ +public class NullMDCProvider implements MDCProvider +{ + public Object get(String key) + { + return null; + } + + public Map getMap() + { + return null; + } + + public void put(String key, Object val) + { + } + + public void remove(String key) + { + } +} \ No newline at end of file Index: 3rdParty_sources/jbossweb/org/jboss/logging/NullNDCProvider.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/logging/NullNDCProvider.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/logging/NullNDCProvider.java 17 Aug 2012 14:43:38 -0000 1.1 @@ -0,0 +1,77 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2005, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jboss.logging; + +import java.util.Stack; + +/** + * An NDC provider which does nothing. + * + * @author Jason T. Greene + */ +public class NullNDCProvider implements NDCProvider +{ + public void clear() + { + } + + public Stack cloneStack() + { + return null; + } + + public String get() + { + return null; + } + + public int getDepth() + { + return 0; + } + + public void inherit(Stack stack) + { + } + + public String peek() + { + return null; + } + + public String pop() + { + return null; + } + + public void push(String message) + { + } + + public void remove() + { + } + + public void setMaxDepth(int maxDepth) + { + } +} Index: 3rdParty_sources/jbossweb/org/jboss/logging/jdk/JDK14LoggerPlugin.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/logging/jdk/JDK14LoggerPlugin.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/logging/jdk/JDK14LoggerPlugin.java 17 Aug 2012 14:43:38 -0000 1.1 @@ -0,0 +1,154 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2005, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.jboss.logging.jdk; + +import java.util.logging.Logger; +import java.util.logging.Level; + +import org.jboss.logging.LoggerPlugin; +import org.jboss.logging.MDCProvider; +import org.jboss.logging.MDCSupport; +import org.jboss.logging.NDCProvider; +import org.jboss.logging.NDCSupport; + +/** An example LoggerPlugin which uses the JDK java.util.logging framework. + * + * @author Scott.Stark@jboss.org + * @version $Revison:$ + */ +public class JDK14LoggerPlugin implements LoggerPlugin, MDCSupport, NDCSupport +{ + private Logger log; + + public void init(String name) + { + log = Logger.getLogger(name); + } + + public boolean isTraceEnabled() + { + return log.isLoggable(Level.FINER); + } + + public void trace(Object message) + { + log(Level.FINER, String.valueOf(message), null); + } + + public void trace(Object message, Throwable t) + { + log(Level.FINER, String.valueOf(message), t); + } + + public boolean isDebugEnabled() + { + return log.isLoggable(Level.FINE); + } + + public void debug(Object message) + { + log(Level.FINE, String.valueOf(message), null); + } + + public void debug(Object message, Throwable t) + { + log(Level.FINE, String.valueOf(message), t); + } + + public boolean isInfoEnabled() + { + return log.isLoggable(Level.INFO); + } + + public void info(Object message) + { + log(Level.INFO, String.valueOf(message), null); + } + + public void info(Object message, Throwable t) + { + log(Level.INFO, String.valueOf(message), t); + } + + public void warn(Object message) + { + log(Level.WARNING, String.valueOf(message), null); + } + + public void warn(Object message, Throwable t) + { + log(Level.WARNING, String.valueOf(message), t); + } + + public void error(Object message) + { + log(Level.SEVERE, String.valueOf(message), null); + } + + public void error(Object message, Throwable t) + { + log(Level.SEVERE, String.valueOf(message), t); + } + + public void fatal(Object message) + { + log(Level.SEVERE, String.valueOf(message), null); + } + + public void fatal(Object message, Throwable t) + { + log(Level.SEVERE, String.valueOf(message), t); + } + + // From commons-logging + private void log(Level level, String msg, Throwable ex) { + if (log.isLoggable(level)) { + // Get the stack trace. + Throwable dummyException = new Throwable(); + StackTraceElement locations[] = dummyException.getStackTrace(); + // Caller will be the third element + String cname = "unknown"; + String method = "unknown"; + if (locations != null && locations.length > 3) { + StackTraceElement caller = locations[3]; + cname = caller.getClassName(); + method = caller.getMethodName(); + } + if (ex == null) { + log.logp(level, cname, method, msg); + } else { + log.logp(level, cname, method, msg, ex); + } + } + } + + public NDCProvider getNDCProvider() + { + return new JDKNDCProvider(); + } + + public MDCProvider getMDCProvider() + { + return new JDKMDCProvider(); + } +} Index: 3rdParty_sources/jbossweb/org/jboss/logging/jdk/JDKMDCProvider.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/logging/jdk/JDKMDCProvider.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/logging/jdk/JDKMDCProvider.java 17 Aug 2012 14:43:38 -0000 1.1 @@ -0,0 +1,68 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2005, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jboss.logging.jdk; + +import java.util.HashMap; +import java.util.Map; + +import org.jboss.logging.MDCProvider; + +/** + * MDC implementation for JDK logging. + * + * @author Jason T. Greene + */ +public class JDKMDCProvider implements MDCProvider +{ + private ThreadLocal> map = new ThreadLocal>(); + + public Object get(String key) + { + return map.get() == null ? null : map.get().get(key); + } + + public Map getMap() + { + return map.get(); + } + + public void put(String key, Object value) + { + Map map = this.map.get(); + if (map == null) + { + map = new HashMap(); + this.map.set(map); + } + + map.put(key, value); + } + + public void remove(String key) + { + Map map = this.map.get(); + if (map == null) + return; + + map.remove(key); + } +} \ No newline at end of file Index: 3rdParty_sources/jbossweb/org/jboss/logging/jdk/JDKNDCProvider.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/logging/jdk/JDKNDCProvider.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/logging/jdk/JDKNDCProvider.java 17 Aug 2012 14:43:38 -0000 1.1 @@ -0,0 +1,149 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2005, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jboss.logging.jdk; + +import java.util.ArrayList; +import java.util.EmptyStackException; +import java.util.Stack; + +import org.jboss.logging.NDCProvider; + +/** + * NDC implementation for JDK logging + * + * @author Jason T. Greene + */ +public class JDKNDCProvider implements NDCProvider +{ + private class ArrayStack extends ArrayList + { + private static final long serialVersionUID = -8520038422243642840L; + + public E pop() + { + int size = size(); + if (size == 0) + throw new EmptyStackException(); + + return remove(size - 1); + } + + public E peek() + { + int size = size(); + if (size == 0) + throw new EmptyStackException(); + + return get(size - 1); + } + + public void push(E val) + { + add(val); + } + + public void setSize(int newSize) + { + int size = size(); + if (newSize >= size || newSize < 0) + return; + + removeRange(newSize, size); + } + } + + private class Entry + { + private String merged; + private String current; + + public Entry(String current) + { + this.merged = current; + this.current = current; + } + + public Entry(Entry parent, String current) + { + this.merged = parent.merged + ' ' + current; + this.current = current; + } + } + + private ThreadLocal> stack = new ThreadLocal>(); + + public void clear() + { + ArrayStack stack = this.stack.get(); + if (stack != null) + stack.clear(); + } + + public String get() + { + ArrayStack stack = this.stack.get(); + + return stack == null || stack.isEmpty() ? null : stack.peek().merged; + } + + public int getDepth() + { + ArrayStack stack = this.stack.get(); + + return stack == null ? 0 : stack.size(); + } + + public String peek() + { + ArrayStack stack = this.stack.get(); + + return stack == null || stack.isEmpty() ? "" : stack.peek().current; + } + + public String pop() + { + ArrayStack stack = this.stack.get(); + + return stack == null || stack.isEmpty() ? "" : stack.pop().current; + } + + public void push(String message) + { + ArrayStack stack = this.stack.get(); + + if (stack == null) + { + stack = new ArrayStack(); + this.stack.set(stack); + } + + stack.push(stack.isEmpty() ? new Entry(message) : new Entry(stack.peek(), message)); + } + + public void setMaxDepth(int maxDepth) + { + ArrayStack stack = this.stack.get(); + + if (stack != null) + stack.setSize(maxDepth); + } +} Index: 3rdParty_sources/jbossweb/org/jboss/servlet/http/HttpEvent.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/servlet/http/HttpEvent.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/servlet/http/HttpEvent.java 17 Aug 2012 14:43:39 -0000 1.1 @@ -0,0 +1,178 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2008, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.jboss.servlet.http; + +import java.io.IOException; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * The HttpEvent interface, which indicates the type of the event that is + * being processed, as well as provides useful callbacks and utility objects. + */ +public interface HttpEvent { + + /** + * Enumeration describing the major events that the container can invoke + * the EventHttpServlet event() method with: + *

+ */ + public enum EventType { BEGIN, END, ERROR, EVENT, READ, EOF, TIMEOUT, WRITE } + + + /** + * Returns the HttpServletRequest. + * + * @return HttpServletRequest + */ + public HttpServletRequest getHttpServletRequest(); + + /** + * Returns the HttpServletResponse. + * + * @return HttpServletResponse + */ + public HttpServletResponse getHttpServletResponse(); + + /** + * Returns the event type. + * + * @return EventType + * @see #EventType + */ + public EventType getType(); + + /** + * Ends the request, which marks the end of the event stream. This will send + * back to the client a notice that the server has no more data to send + * as part of this request. An END event will be sent to the Servlet. + * + * @throws IOException if an IO exception occurs + */ + public void close() throws IOException; + + /** + * This method sets the timeout in milliseconds of idle time on the connection. + * The timeout is reset every time data is received from the connection. If a timeout occurs, the + * Servlet will receive an TIMEOUT event which will not result in automatically closing + * the event (the event may be closed using the close() method). + * + * @param timeout The timeout in milliseconds for this connection, must be a positive value, larger than 0 + */ + public void setTimeout(int timeout); + + /** + * Returns true when data may be read from the connection (the flag becomes false if no data + * is available to read). When the flag becomes false, the Servlet can attempt to read additional + * data, but it will block until data is available. This method is equivalent to + * Reader.ready() and (InputStream.available() > 0). + * + * @return boolean true if data can be read without blocking + */ + public boolean isReadReady(); + + /** + * Returns true when data may be written to the connection (the flag becomes false + * when the client is unable to accept data fast enough). When the flag becomes false, + * the Servlet must stop writing data. If there's an attempt to flush additional data + * to the client and data still cannot be written immediately, an IOException will be + * thrown. If calling this method returns false, it will also + * request notification when the connection becomes available for writing again, and the + * Servlet will receive a write event. + *
+ * Note: If the Servlet is not using isWriteReady, and is writing its output inside the + * container threads (inside the event() method processing, for example), using this method + * is not mandatory, and writes will block until all bytes are written. + * + * @return boolean true if data can be written without blocking + */ + public boolean isWriteReady(); + + /** + * Suspend processing of the connection until the configured timeout occurs, + * or resume() is called. In practice, this means the servlet will no longer + * receive read events. Reading should always be performed synchronously in + * the Tomcat threads unless the connection has been suspended. + */ + public void suspend(); + + /** + * Resume will cause the Servlet container to send a generic event + * to the Servlet, where the request can be processed synchronously + * (for example, it is possible to use this to complete the request after + * some asynchronous processing is done). This also resumes read events + * if they have been disabled using suspend. It is then possible to call suspend + * again later. It is also possible to call resume without calling suspend before. + * This method must be called asynchronously. + */ + public void resume(); + +} Index: 3rdParty_sources/jbossweb/org/jboss/servlet/http/HttpEventFilter.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/servlet/http/HttpEventFilter.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/servlet/http/HttpEventFilter.java 17 Aug 2012 14:43:39 -0000 1.1 @@ -0,0 +1,66 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2008, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.jboss.servlet.http; + +import java.io.IOException; + +import javax.servlet.Filter; +import javax.servlet.ServletException; + +/** + * An event filter, similar to regular filters, performs filtering tasks on either + * the request to a resource (an event driven Servlet), or on the response from a resource, or both. + */ +public interface HttpEventFilter extends Filter { + + + /** + * The doFilterEvent method of the HttpEventFilter is called by the container + * each time a request/response pair is passed through the chain due + * to a client event for a resource at the end of the chain. The HttpEventFilterChain passed in to this + * method allows the Filter to pass on the event to the next entity in the + * chain.

+ * A typical implementation of this method would follow the following pattern:-
+ * 1. Examine the request
+ * 2. Optionally wrap the request object contained in the event with a custom implementation to + * filter content or headers for input filtering and pass a HttpEvent instance containing + * the wrapped request to the next filter
+ * 3. Optionally wrap the response object contained in the event with a custom implementation to + * filter content or headers for output filtering and pass a HttpEvent instance containing + * the wrapped request to the next filter
+ * 4. a) Either invoke the next entity in the chain using the HttpEventFilterChain + * object (chain.doFilterEvent()),
+ * 4. b) or not pass on the request/response pair to the next entity in the + * filter chain to block the event processing
+ * 5. Directly set fields on the response after invocation of the next entity in the filter chain. + * + * @param event the event that is being processed. Another event may be passed along the chain. + * @param chain + * @throws IOException + * @throws ServletException + */ + public void doFilterEvent(HttpEvent event, HttpEventFilterChain chain) + throws IOException, ServletException; + + +} Index: 3rdParty_sources/jbossweb/org/jboss/servlet/http/HttpEventFilterChain.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/servlet/http/HttpEventFilterChain.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/servlet/http/HttpEventFilterChain.java 17 Aug 2012 14:43:39 -0000 1.1 @@ -0,0 +1,47 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2008, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.jboss.servlet.http; + +import java.io.IOException; + +import javax.servlet.ServletException; + +/** + * A HttpEventFilterChain is an object provided by the Servlet container to the developer + * giving a view into the invocation chain of a filtered event for a resource. Filters + * use the HttpEventFilterChain to invoke the next filter in the chain, or if the calling filter + * is the last filter in the chain, to invoke the resource at the end of the chain. + */ +public interface HttpEventFilterChain { + + + /** + * Causes the next filter in the chain to be invoked, or if the calling filter is the last filter + * in the chain, causes the resource at the end of the chain to be invoked. + * + * @param event the event to pass along the chain. + */ + public void doFilterEvent(HttpEvent event) throws IOException, ServletException; + + +} Index: 3rdParty_sources/jbossweb/org/jboss/servlet/http/HttpEventServlet.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/servlet/http/HttpEventServlet.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/servlet/http/HttpEventServlet.java 17 Aug 2012 14:43:39 -0000 1.1 @@ -0,0 +1,50 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2008, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.jboss.servlet.http; + +import java.io.IOException; + +import javax.servlet.Servlet; +import javax.servlet.ServletException; + +/** + * This interface should be implemented by Servlets which would like to handle + * asynchronous IO, receiving events when data is available for reading, and + * being able to output data without the need for being invoked by the container. + * Note: When this interface is implemented, the service method of the Servlet will + * never be called, and will be replaced with a begin event. + */ +public interface HttpEventServlet extends Servlet +{ + + /** + * Process the given IO event. + * + * @param event The event that will be processed + * @throws IOException + * @throws ServletException + */ + public void event(HttpEvent event) + throws IOException, ServletException; + +} Index: 3rdParty_sources/jbossweb/org/jboss/web/cluster/ClusterListener.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/web/cluster/ClusterListener.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/web/cluster/ClusterListener.java 17 Aug 2012 14:43:39 -0000 1.1 @@ -0,0 +1,1557 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2008, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + + +package org.jboss.web.cluster; + + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.net.InetAddress; +import java.net.Socket; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.StringTokenizer; + +import javax.management.ObjectName; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.TrustManagerFactory; + +import org.apache.catalina.Container; +import org.apache.catalina.ContainerEvent; +import org.apache.catalina.ContainerListener; +import org.apache.catalina.Context; +import org.apache.catalina.Engine; +import org.apache.catalina.Host; +import org.apache.catalina.Lifecycle; +import org.apache.catalina.LifecycleEvent; +import org.apache.catalina.LifecycleListener; +import org.apache.catalina.Server; +import org.apache.catalina.ServerFactory; +import org.apache.catalina.Service; +import org.apache.catalina.connector.Connector; +import org.apache.catalina.core.StandardContext; +import org.apache.catalina.core.StandardServer; +import org.apache.catalina.util.StringManager; +import org.apache.tomcat.util.IntrospectionUtils; +import org.apache.tomcat.util.buf.CharChunk; +import org.apache.tomcat.util.buf.UEncoder; +import org.apache.tomcat.util.modeler.Registry; +import org.jboss.logging.Logger; +import org.jboss.web.cluster.advertise.AdvertiseListener; + + + +/** + * This listener communicates with a front end mod_cluster enabled proxy to + * automatically maintain the node configuration according to what is + * deployed. + */ +public class ClusterListener + implements LifecycleListener, ContainerListener { + + protected static Logger log = Logger.getLogger(ClusterListener.class); + + /** + * The string manager for this package. + */ + protected StringManager sm = + StringManager.getManager(Constants.Package); + + + // -------------------------------------------------------------- Constants + + + protected enum State { OK, ERROR, DOWN }; + + + // ----------------------------------------------------------------- Fields + + + /** + * URL encoder used to generate requests bodies. + */ + protected UEncoder encoder = new UEncoder(); + + + /** + * JMX registration information. + */ + protected ObjectName oname; + + + /** + * Proxies. + */ + protected Proxy[] proxies = new Proxy[0]; + + + /** + * Socket factory. + */ + protected JSSESocketFactory sslSocketFactory = null; + + + /** + * Active connections. + */ + protected Socket[] connections = null; + + + /** + * Connection readers. + */ + protected BufferedReader[] connectionReaders = null; + + + /** + * Connection writers. + */ + protected BufferedWriter[] connectionWriters = null; + + + /** + * Initialization flag. + */ + protected boolean init = false; + + + /** + * Add proxy list. + */ + protected ArrayList addProxies = new ArrayList(); + + + /** + * Remove proxy list. + */ + protected ArrayList removeProxies = new ArrayList(); + + + /** + * Advertise listener. + */ + protected AdvertiseListener listener = null; + + + // ------------------------------------------------------------- Properties + + + /** + * Receive advertisements from httpd proxies (default is to use advertisements + * if the proxyList is not set). + */ + protected int advertise = -1; + public boolean getAdvertise() { return (advertise == 0) ? false : true; } + public void setAdvertise(boolean advertise) { this.advertise = advertise ? 1 : 0; } + + + /** + * Advertise group. + */ + protected String advertiseGroupAddress = null; + public String getAdvertiseGroupAddress() { return advertiseGroupAddress; } + public void setAdvertiseGroupAddress(String advertiseGroupAddress) { this.advertiseGroupAddress = advertiseGroupAddress; } + + + /** + * Advertise port. + */ + protected int advertisePort = -1; + public int getAdvertisePort() { return advertisePort; } + public void setAdvertisePort(int advertisePort) { this.advertisePort = advertisePort; } + + + /** + * Advertise security key. + */ + protected String advertiseSecurityKey = null; + public String getAdvertiseSecurityKey() { return advertiseSecurityKey; } + public void setAdvertiseSecurityKey(String advertiseSecurityKey) { this.advertiseSecurityKey = advertiseSecurityKey; } + + + /** + * Proxy list, format "address:port,address:port". + */ + protected String proxyList = null; + public String getProxyList() { return proxyList; } + public void setProxyList(String proxyList) { this.proxyList = proxyList; } + + + /** + * URL prefix. + */ + protected String proxyURL = null; + public String getProxyURL() { return proxyURL; } + public void setProxyURL(String proxyURL) { this.proxyURL = proxyURL; } + + + /** + * Connection timeout for communication with the proxy. + */ + protected int socketTimeout = 20000; + public int getSocketTimeout() { return socketTimeout; } + public void setSocketTimeout(int socketTimeout) { this.socketTimeout = socketTimeout; } + + + /** + * Domain parameter, which allows tying a jvmRoute to a particular domain. + */ + protected String domain = null; + public String getDomain() { return domain; } + public void setDomain(String domain) { this.domain = domain; } + + + /** + * Allows controlling flushing of packets. + */ + protected boolean flushPackets = false; + public boolean getFlushPackets() { return flushPackets; } + public void setFlushPackets(boolean flushPackets) { this.flushPackets = flushPackets; } + + + /** + * Time to wait before flushing packets. + */ + protected int flushWait = -1; + public int getFlushWait() { return flushWait; } + public void setFlushWait(int flushWait) { this.flushWait = flushWait; } + + + /** + * Time to wait for a pong answer to a ping. + */ + protected int ping = -1; + public int getPing() { return ping; } + public void setPing(int ping) { this.ping = ping; } + + + /** + * Soft maximum inactive connection count. + */ + protected int smax = -1; + public int getSmax() { return smax; } + public void setSmax(int smax) { this.smax = smax; } + + + /** + * Maximum time on seconds for idle connections above smax. + */ + protected int ttl = -1; + public int getTtl() { return ttl; } + public void setTtl(int ttl) { this.ttl = ttl; } + + + /** + * Maximum time on seconds for idle connections the proxy will wait to connect to the node. + */ + protected int nodeTimeout = -1; + public int getNodeTimeout() { return nodeTimeout; } + public void setNodeTimeout(int nodeTimeout) { this.nodeTimeout = nodeTimeout; } + + + /** + * Name of the balancer. + */ + protected String balancer = null; + public String getBalancer() { return balancer; } + public void setBalancer(String balancer) { this.balancer = balancer; } + + + /** + * Enables sticky sessions. + */ + protected boolean stickySession = true; + public boolean getStickySession() { return stickySession; } + public void setStickySession(boolean stickySession) { this.stickySession = stickySession; } + + + /** + * Remove session when the request cannot be routed to the right node. + */ + protected boolean stickySessionRemove = false; + public boolean getStickySessionRemove() { return stickySessionRemove; } + public void setStickySessionRemove(boolean stickySessionRemove) { this.stickySessionRemove = stickySessionRemove; } + + + /** + * Return an error when the request cannot be routed to the right node. + */ + protected boolean stickySessionForce = true; + public boolean getStickySessionForce() { return stickySessionForce; } + public void setStickySessionForce(boolean stickySessionForce) { this.stickySessionForce = stickySessionForce; } + + + /** + * Timeout to wait for an available worker (default is no wait). + */ + protected int workerTimeout = -1; + public int getWorkerTimeout() { return workerTimeout; } + public void setWorkerTimeout(int workerTimeout) { this.workerTimeout = workerTimeout; } + + + /** + * Maximum number of attempts to send the request to the backend server. + */ + protected int maxAttempts = -1; + public int getMaxAttempts() { return maxAttempts; } + public void setMaxAttempts(int maxAttempts) { this.maxAttempts = maxAttempts; } + + + /** + * SSL client cert usage to connect to the proxy. + */ + protected boolean ssl = false; + public boolean isSsl() { return ssl; } + public void setSsl(boolean ssl) { this.ssl = ssl; } + + + /** + * SSL ciphers. + */ + protected String sslCiphers = null; + public String getSslCiphers() { return sslCiphers; } + public void setSslCiphers(String sslCiphers) { this.sslCiphers = sslCiphers; } + + + /** + * SSL protocol. + */ + protected String sslProtocol = "TLS"; + public String getSslProtocol() { return sslProtocol; } + public void setSslProtocol(String sslProtocol) { this.sslProtocol = sslProtocol; } + + + /** + * Certificate encoding algorithm. + */ + protected String sslCertificateEncodingAlgorithm = KeyManagerFactory.getDefaultAlgorithm(); + public String getSslCertificateEncodingAlgorithm() { return sslCertificateEncodingAlgorithm; } + public void setSslCertificateEncodingAlgorithm(String sslCertificateEncodingAlgorithm) { this.sslCertificateEncodingAlgorithm = sslCertificateEncodingAlgorithm; } + + + /** + * SSL keystore. + */ + protected String sslKeyStore = System.getProperty("user.home") + "/.keystore"; + public String getSslKeyStore() { return sslKeyStore; } + public void setSslKeyStore(String sslKeyStore) { this.sslKeyStore = sslKeyStore; } + + + /** + * SSL keystore password. + */ + protected String sslKeyStorePass = "changeit"; + public String getSslKeyStorePass() { return sslKeyStorePass; } + public void setSslKeyStorePass(String sslKeyStorePass) { this.sslKeyStorePass = sslKeyStorePass; } + + + /** + * Keystore type. + */ + protected String sslKeyStoreType = "JKS"; + public String getSslKeyStoreType() { return sslKeyStoreType; } + public void setSslKeyStoreType(String sslKeyStoreType) { this.sslKeyStoreType = sslKeyStoreType; } + + + /** + * Keystore provider. + */ + protected String sslKeyStoreProvider = null; + public String getSslKeyStoreProvider() { return sslKeyStoreProvider; } + public void setSslKeyStoreProvider(String sslKeyStoreProvider) { this.sslKeyStoreProvider = sslKeyStoreProvider; } + + + /** + * Truststore algorithm. + */ + protected String sslTrustAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); + public String getSslTrustAlgorithm() { return sslTrustAlgorithm; } + public void setSslTrustAlgorithm(String sslTrustAlgorithm) { this.sslTrustAlgorithm = sslTrustAlgorithm; } + + + /** + * Key alias. + */ + protected String sslKeyAlias = null; + public String getSslKeyAlias() { return sslKeyAlias; } + public void setSslKeyAlias(String sslKeyAlias) { this.sslKeyAlias = sslKeyAlias; } + + + /** + * Certificate revocation list. + */ + protected String sslCrlFile = null; + public String getSslCrlFile() { return sslCrlFile; } + public void setSslCrlFile(String sslCrlFile) { this.sslCrlFile = sslCrlFile; } + + + /** + * Trust max certificate length. + */ + protected int sslTrustMaxCertLength = 5; + public int getSslTrustMaxCertLength() { return sslTrustMaxCertLength; } + public void setSslTrustMaxCertLength(int sslTrustMaxCertLength) { this.sslTrustMaxCertLength = sslTrustMaxCertLength; } + + + /** + * Trust store file. + */ + protected String sslTrustStore = System.getProperty("javax.net.ssl.trustStore"); + public String getSslTrustStore() { return sslTrustStore; } + public void setSslTrustStore(String sslTrustStore) { this.sslTrustStore = sslTrustStore; } + + + /** + * Trust store password. + */ + protected String sslTrustStorePassword = System.getProperty("javax.net.ssl.trustStorePassword"); + public String getSslTrustStorePassword() { return sslTrustStorePassword; } + public void setSslTrustStorePassword(String sslTrustStorePassword) { this.sslTrustStorePassword = sslTrustStorePassword; } + + + /** + * Trust store type. + */ + protected String sslTrustStoreType = System.getProperty("javax.net.ssl.trustStoreType"); + public String getSslTrustStoreType() { return sslTrustStoreType; } + public void setSslTrustStoreType(String sslTrustStoreType) { this.sslTrustStoreType = sslTrustStoreType; } + + + /** + * Trust store provider. + */ + protected String sslTrustStoreProvider = System.getProperty("javax.net.ssl.trustStoreProvider"); + public String getSslTrustStoreProvider() { return sslTrustStoreProvider; } + public void setSslTrustStoreProvider(String sslTrustStoreProvider) { this.sslTrustStoreProvider = sslTrustStoreProvider; } + + + // ---------------------------------------------- LifecycleListener Methods + + + /** + * Acknowledge the occurrence of the specified event. + * Note: Will never be called when the listener is associated to a Server, + * since it is not a Container. + * + * @param event ContainerEvent that has occurred + */ + public void containerEvent(ContainerEvent event) { + + Container container = event.getContainer(); + Object child = event.getData(); + String type = event.getType(); + + if (type.equals(Container.ADD_CHILD_EVENT)) { + if (container instanceof Host) { + // Deploying a webapp + ((Lifecycle) child).addLifecycleListener(this); + addContext((Context) child, -1); + } else if (container instanceof Engine) { + // Deploying a host + container.addContainerListener(this); + } + } else if (type.equals(Container.REMOVE_CHILD_EVENT)) { + if (container instanceof Host) { + // Undeploying a webapp + ((Lifecycle) child).removeLifecycleListener(this); + removeContext((Context) child, -1); + } else if (container instanceof Engine) { + // Undeploying a host + container.removeContainerListener(this); + } + } + + } + + + /** + * Primary entry point for startup and shutdown events. + * + * @param event The event that has occurred + */ + public void lifecycleEvent(LifecycleEvent event) { + + Object source = event.getLifecycle(); + + if (Lifecycle.START_EVENT.equals(event.getType())) { + if (source instanceof Context) { + // Start a webapp + startContext((Context) source, -1); + } else { + return; + } + } else if (Lifecycle.AFTER_START_EVENT.equals(event.getType())) { + if (source instanceof Server) { + + if (this.proxyList == null) { + if (advertise != 0) { + proxies = new Proxy[0]; + startListener(); + } else { + // Default to a httpd on localhost on the default port + proxies = new Proxy[1]; + proxies[0] = new Proxy(); + } + } else { + ArrayList proxyList = new ArrayList(); + StringTokenizer tok = new StringTokenizer(this.proxyList, ","); + while (tok.hasMoreTokens()) { + String token = tok.nextToken().trim(); + Proxy proxy = new Proxy(); + int pos = token.indexOf(':'); + String address = null; + if (pos < 0) { + address = token; + } else if (pos == 0) { + address = null; + proxy.port = Integer.parseInt(token.substring(1)); + } else { + address = token.substring(0, pos); + proxy.port = Integer.parseInt(token.substring(pos + 1)); + } + try { + if (address != null) { + proxy.address = InetAddress.getByName(address); + } + } catch (Exception e) { + log.error(sm.getString("clusterListener.error.invalidHost", address), e); + continue; + } + proxyList.add(proxy); + } + proxies = proxyList.toArray(new Proxy[0]); + } + + connections = new Socket[proxies.length]; + connectionReaders = new BufferedReader[proxies.length]; + connectionWriters = new BufferedWriter[proxies.length]; + + sslInit(); + startServer((Server) source, -1); + + if (advertise == 1) { + startListener(); + } + + init = true; + } else { + return; + } + } else if (Lifecycle.STOP_EVENT.equals(event.getType())) { + if (source instanceof Context) { + // Stop a webapp + stopContext((Context) source, -1); + } else if (source instanceof Server) { + stopListener(); + stopServer((Server) source, -1); + for (int i = 0; i < connections.length; i++) { + closeConnection(i); + } + init = false; + } else { + return; + } + } else if (Lifecycle.PERIODIC_EVENT.equals(event.getType())) { + if (init && source instanceof Engine) { + status((Engine) source); + } + } + + } + + + /** + * Add proxy. + */ + public void addProxy(String address) { + int pos = address.indexOf(':'); + String host = null; + int port = 0; + if (pos < 0) { + host = address; + } else if (pos == 0) { + host = null; + port = Integer.parseInt(address.substring(1)); + } else { + host = address.substring(0, pos); + port = Integer.parseInt(address.substring(pos + 1)); + } + addProxy(host, port); + } + + + /** + * Add proxy. + */ + public synchronized void addProxy(String host, int port) { + Proxy proxy = new Proxy(); + try { + proxy.address = InetAddress.getByName(host); + } catch (Exception e) { + throw new IllegalArgumentException(e); + } + if (port > 0) { + proxy.port = port; + } + proxy.state = State.ERROR; + addProxies.add(proxy); + } + + + /** + * Remove proxy. + */ + public synchronized void removeProxy(String host, int port) { + Proxy proxy = new Proxy(); + try { + proxy.address = InetAddress.getByName(host); + } catch (Exception e) { + throw new IllegalArgumentException(e); + } + if (port > 0) { + proxy.port = port; + } + removeProxies.add(proxy); + } + + + /** + * Retrieves the full proxy configuration. To be used through JMX or similar. + * + * response: HTTP/1.1 200 OK + * response: + * node: [1:1] JVMRoute: node1 Domain: [bla] Host: 127.0.0.1 Port: 8009 Type: ajp + * host: 1 [] vhost: 1 node: 1 + * context: 1 [/] vhost: 1 node: 1 status: 1 + * context: 2 [/myapp] vhost: 1 node: 1 status: 1 + * context: 3 [/host-manager] vhost: 1 node: 1 status: 1 + * context: 4 [/docs] vhost: 1 node: 1 status: 1 + * context: 5 [/manager] vhost: 1 node: 1 status: 1 + * + * @return the proxy confguration + */ + public String getProxyConfiguration() { + HashMap parameters = new HashMap(); + // Send DUMP * request + Proxy[] local = proxies; + StringBuffer result = new StringBuffer(); + for (int i = 0; i < local.length; i++) { + result.append("Proxy[").append(i).append("]: [").append(local[i].address) + .append(':').append(local[i].port).append("]: \r\n"); + result.append(sendRequest("DUMP", true, parameters, i)); + result.append("\r\n"); + } + return result.toString(); + } + /** + * Retrieves the full proxy info message. + * + * + * @return the proxy confguration + */ + public String getProxyInfo() { + HashMap parameters = new HashMap(); + // Send INFO * request + Proxy[] local = proxies; + StringBuffer result = new StringBuffer(); + for (int i = 0; i < local.length; i++) { + result.append("Proxy[").append(i).append("]: [").append(local[i].address) + .append(':').append(local[i].port).append("]: \r\n"); + result.append(sendRequest("INFO", true, parameters, i)); + result.append("\r\n"); + } + return result.toString(); + } + + + /** + * Reset a DOWN connection to the proxy up to ERROR, where the configuration will + * be refreshed. To be used through JMX or similar. + */ + public void reset() { + Proxy[] local = proxies; + for (int i = 0; i < local.length; i++) { + if (local[i].state == State.DOWN) { + local[i].state = State.ERROR; + } + } + } + + + /** + * Refresh configuration. To be used through JMX or similar. + */ + public void refresh() { + // Set as error, and the periodic event will refresh the configuration + Proxy[] local = proxies; + for (int i = 0; i < local.length; i++) { + if (local[i].state == State.OK) { + local[i].state = State.ERROR; + } + } + } + + + /** + * Disable all webapps for all engines. To be used through JMX or similar. + */ + public boolean disable() { + Service[] services = ServerFactory.getServer().findServices(); + for (int i = 0; i < services.length; i++) { + Engine engine = (Engine) services[i].getContainer(); + HashMap parameters = new HashMap(); + parameters.put("JVMRoute", engine.getJvmRoute()); + // Send DISABLE-APP * request + sendRequest("DISABLE-APP", true, parameters); + } + return (proxies[0].state == State.OK); + } + + + /** + * Enable all webapps for all engines. To be used through JMX or similar. + */ + public boolean enable() { + Service[] services = ServerFactory.getServer().findServices(); + for (int i = 0; i < services.length; i++) { + Engine engine = (Engine) services[i].getContainer(); + HashMap parameters = new HashMap(); + parameters.put("JVMRoute", engine.getJvmRoute()); + // Send ENABLE-APP * request + sendRequest("ENABLE-APP", true, parameters); + } + return (proxies[0].state == State.OK); + } + + + /** + * Start the advertise listener. + */ + protected void startListener() { + listener = new AdvertiseListener(this); + if (advertiseGroupAddress != null) { + listener.setGroupAddress(advertiseGroupAddress); + } + if (advertisePort > 0) { + listener.setAdvertisePort(advertisePort); + } + try { + if (advertiseSecurityKey != null) { + listener.setSecurityKey(advertiseSecurityKey); + } + listener.start(); + } catch (IOException e) { + log.error(sm.getString("clusterListener.error.startListener"), e); + } catch (NoSuchAlgorithmException e) { + // Should never happen + log.error(sm.getString("clusterListener.error.startListener"), e); + } + } + + + /** + * Stop the advertise listener. + */ + protected void stopListener() { + if (listener != null) { + try { + listener.destroy(); + } catch (IOException e) { + log.error(sm.getString("clusterListener.error.stopListener"), e); + } + listener = null; + } + } + + + /** + * Send commands to the front end server assocaited with the startup of the + * node. + */ + protected void startServer(Server server, int pos) { + + // JMX registration + if (oname==null) { + try { + oname = new ObjectName(((StandardServer) server).getDomain() + ":type=ClusterListener"); + Registry.getRegistry(null, null).registerComponent(this, oname, null); + } catch (Exception e) { + log.error(sm.getString("clusterListener.error.jmxRegister"), e); + } + } + + Service[] services = server.findServices(); + for (int i = 0; i < services.length; i++) { + services[i].getContainer().addContainerListener(this); + + Engine engine = (Engine) services[i].getContainer(); + ((Lifecycle) engine).addLifecycleListener(this); + Connector connector = findProxyConnector(engine.getService().findConnectors()); + InetAddress localAddress = + (InetAddress) IntrospectionUtils.getProperty(connector.getProtocolHandler(), "address"); + if ((engine.getJvmRoute() == null || localAddress == null) && proxies.length > 0) { + // Automagical JVM route (address + port + engineName) + try { + if (localAddress == null) { + Socket connection = getConnection(0); + localAddress = connection.getLocalAddress(); + if (localAddress != null) { + IntrospectionUtils.setProperty(connector.getProtocolHandler(), "address", localAddress.getHostAddress()); + } else { + // Should not happen + IntrospectionUtils.setProperty(connector.getProtocolHandler(), "address", "127.0.0.1"); + } + log.info(sm.getString("clusterListener.address", localAddress.getHostAddress())); + } + if (engine.getJvmRoute() == null) { + String hostName = null; + if (localAddress != null) { + hostName = localAddress.getHostName(); + } else { + // Fallback + hostName = "127.0.0.1"; + } + String jvmRoute = hostName + ":" + connector.getPort() + ":" + engine.getName(); + engine.setJvmRoute(jvmRoute); + log.info(sm.getString("clusterListener.jvmRoute", engine.getName(), jvmRoute)); + } + } catch (Exception e) { + proxies[0].state = State.ERROR; + log.info(sm.getString("clusterListener.error.addressJvmRoute"), e); + return; + } + } + + config(engine, pos); + Container[] children = engine.findChildren(); + for (int j = 0; j < children.length; j++) { + children[j].addContainerListener(this); + Container[] children2 = children[j].findChildren(); + for (int k = 0; k < children2.length; k++) { + ((Lifecycle) children2[k]).addLifecycleListener(this); + addContext((Context) children2[k], pos); + } + } + } + } + + + /** + * Send commands to the front end server associated with the shutdown of the + * node. + */ + protected void stopServer(Server server, int pos) { + + // JMX unregistration + if (oname==null) { + try { + Registry.getRegistry(null, null).unregisterComponent(oname); + } catch (Exception e) { + log.error(sm.getString("clusterListener.error.jmxUnregister"), e); + } + } + + Service[] services = server.findServices(); + for (int i = 0; i < services.length; i++) { + services[i].getContainer().removeContainerListener(this); + ((Lifecycle) services[i].getContainer()).removeLifecycleListener(this); + removeAll((Engine) services[i].getContainer(), pos); + Container[] children = services[i].getContainer().findChildren(); + for (int j = 0; j < children.length; j++) { + children[j].removeContainerListener(this); + Container[] children2 = children[j].findChildren(); + for (int k = 0; k < children2.length; k++) { + ((Lifecycle) children2[k]).removeLifecycleListener(this); + removeContext((Context) children2[k], pos); + } + } + } + } + + + /** + * Reset configuration for a particular proxy following an error. + */ + protected void reset(int pos) { + + Service[] services = ServerFactory.getServer().findServices(); + for (int i = 0; i < services.length; i++) { + Engine engine = (Engine) services[i].getContainer(); + removeAll((Engine) services[i].getContainer(), pos); + config(engine, pos); + Container[] children = engine.findChildren(); + for (int j = 0; j < children.length; j++) { + Container[] children2 = children[j].findChildren(); + for (int k = 0; k < children2.length; k++) { + addContext((Context) children2[k], pos); + } + } + } + + } + + + /** + * Send the configuration for the specified engine to the proxy. + * + * @param engine + */ + protected void config(Engine engine, int pos) { + if (log.isDebugEnabled()) { + log.debug(sm.getString("clusterListener.config", engine.getName())); + } + // Collect configuration from the connectors and service and call CONFIG + Connector connector = findProxyConnector(engine.getService().findConnectors()); + HashMap parameters = new HashMap(); + parameters.put("JVMRoute", engine.getJvmRoute()); + boolean reverseConnection = + Boolean.TRUE.equals(IntrospectionUtils.getProperty(connector.getProtocolHandler(), "reverseConnection")); + boolean ssl = + Boolean.TRUE.equals(IntrospectionUtils.getProperty(connector.getProtocolHandler(), "SSLEnabled")); + boolean ajp = ((String) IntrospectionUtils.getProperty(connector.getProtocolHandler(), "name")).startsWith("ajp-"); + + if (reverseConnection) { + parameters.put("Reversed", "true"); + } + parameters.put("Host", getAddress(connector)); + parameters.put("Port", "" + connector.getPort()); + if (ajp) { + parameters.put("Type", "ajp"); + } else if (ssl) { + parameters.put("Type", "https"); + } else { + parameters.put("Type", "http"); + } + + // Other configuration parameters + if (domain != null) { + parameters.put("Domain", domain); + } + if (flushPackets) { + parameters.put("flushpackets", "On"); + } + if (flushWait != -1) { + parameters.put("flushwait", "" + flushWait); + } + if (ping != -1) { + parameters.put("ping", "" + ping); + } + if (smax != -1) { + parameters.put("smax", "" + smax); + } + if (ttl != -1) { + parameters.put("ttl", "" + ttl); + } + if (nodeTimeout != -1) { + parameters.put("Timeout", "" + nodeTimeout); + } + if (balancer != null) { + parameters.put("Balancer", balancer); + } + if (!stickySession) { + parameters.put("StickySession", "No"); + } + if (!org.apache.catalina.Globals.SESSION_COOKIE_NAME.equals("JSESSIONID")) { + parameters.put("StickySessionCookie", org.apache.catalina.Globals.SESSION_COOKIE_NAME); + } + if (!org.apache.catalina.Globals.SESSION_PARAMETER_NAME.equals("jsessionid")) { + parameters.put("StickySessionPath", org.apache.catalina.Globals.SESSION_PARAMETER_NAME); + } + if (stickySessionRemove) { + parameters.put("StickSessionRemove", "Yes"); + } + if (!stickySessionForce) { + parameters.put("StickySessionForce", "No"); + } + if (workerTimeout != -1) { + parameters.put("WaitWorker", "" + workerTimeout); + } + if (maxAttempts != -1) { + parameters.put("Maxattempts", "" + maxAttempts); + } + + // Send CONFIG request + sendRequest("CONFIG", false, parameters, pos); + } + + + /** + * Remove all contexts from the specified engine. + * + * @param engine + */ + protected void removeAll(Engine engine, int pos) { + if (log.isDebugEnabled()) { + log.debug(sm.getString("clusterListener.stop", engine.getName())); + } + + // JVMRoute can be null here if nothing was ever initialized + if (engine.getJvmRoute() != null) { + HashMap parameters = new HashMap(); + parameters.put("JVMRoute", engine.getJvmRoute()); + + // Send REMOVE-APP * request + sendRequest("REMOVE-APP", true, parameters, pos); + } + } + + + /** + * Send a periodic status request. If in error state, the listener will attempt to refresh + * the configuration on the front end server. + * + * @param engine + */ + protected synchronized void status(Engine engine) { + + // Check to add or remove proxies, and rebuild a new list if needed + if (!addProxies.isEmpty() || !removeProxies.isEmpty()) { + ArrayList currentProxies = new ArrayList(); + for (int i = 0; i < proxies.length; i++) { + currentProxies.add(proxies[i]); + } + for (int i = 0; i < addProxies.size(); i++) { + if (!currentProxies.contains(addProxies.get(i))) { + currentProxies.add(addProxies.get(i)); + } + } + for (int i = 0; i < removeProxies.size(); i++) { + if (currentProxies.contains(removeProxies.get(i))) { + currentProxies.remove(removeProxies.get(i)); + } + } + addProxies.clear(); + removeProxies.clear(); + proxies = currentProxies.toArray(new Proxy[0]); + // Reset all connections + if (connections != null) { + for (int i = 0; i < connections.length; i++) { + closeConnection(i); + } + } + connections = new Socket[proxies.length]; + connectionReaders = new BufferedReader[proxies.length]; + connectionWriters = new BufferedWriter[proxies.length]; + } + + Proxy[] local = proxies; + for (int i = 0; i < local.length; i++) { + if (local[i].state == State.ERROR) { + local[i].state = State.OK; + // Something went wrong in a status at some point, so fully restore the configuration + reset(i); + } else if (local[i].state == State.OK) { + if (log.isDebugEnabled()) { + log.debug(sm.getString("clusterListener.status", engine.getName())); + } + HashMap parameters = new HashMap(); + parameters.put("JVMRoute", engine.getJvmRoute()); + parameters.put("Load", "1"); + // Send STATUS request + sendRequest("STATUS", false, parameters, i); + } + } + } + + + /** + * Add a new context. + * + * @param context + */ + protected void addContext(Context context, int pos) { + if (log.isDebugEnabled()) { + log.debug(sm.getString("clusterListener.context.enable", context.getPath(), context.getParent().getName(), ((StandardContext) context).getState())); + } + + HashMap parameters = new HashMap(); + parameters.put("JVMRoute", getJvmRoute(context)); + parameters.put("Context", ("".equals(context.getPath())) ? "/" : context.getPath()); + parameters.put("Alias", getHost(context)); + + // Send ENABLE-APP if state is started + if (context.isStarted()) { + sendRequest("ENABLE-APP", false, parameters, pos); + } + } + + + /** + * Remove a context. + * + * @param context + */ + protected void removeContext(Context context, int pos) { + if (log.isDebugEnabled()) { + log.debug(sm.getString("clusterListener.context.disable", context.getPath(), context.getParent().getName(), ((StandardContext) context).getState())); + } + + HashMap parameters = new HashMap(); + String jvmRoute = getJvmRoute(context); + // JVMRoute can be null here if nothing was ever initialized + if (jvmRoute != null) { + parameters.put("JVMRoute", jvmRoute); + parameters.put("Context", ("".equals(context.getPath())) ? "/" : context.getPath()); + parameters.put("Alias", getHost(context)); + + // Send REMOVE-APP + sendRequest("REMOVE-APP", false, parameters, pos); + } + } + + + /** + * Start a context. + * + * @param context + */ + protected void startContext(Context context, int pos) { + if (log.isDebugEnabled()) { + log.debug(sm.getString("clusterListener.context.start", context.getPath(), context.getParent().getName())); + } + + HashMap parameters = new HashMap(); + parameters.put("JVMRoute", getJvmRoute(context)); + parameters.put("Context", ("".equals(context.getPath())) ? "/" : context.getPath()); + parameters.put("Alias", getHost(context)); + + // Send ENABLE-APP + sendRequest("ENABLE-APP", false, parameters, pos); + } + + + /** + * Stop a context. + * + * @param context + */ + protected void stopContext(Context context, int pos) { + if (log.isDebugEnabled()) { + log.debug(sm.getString("clusterListener.context.stop", context.getPath(), context.getParent().getName())); + } + + HashMap parameters = new HashMap(); + parameters.put("JVMRoute", getJvmRoute(context)); + parameters.put("Context", ("".equals(context.getPath())) ? "/" : context.getPath()); + parameters.put("Alias", getHost(context)); + + // Send STOP-APP + sendRequest("STOP-APP", false, parameters, pos); + } + + + /** + * Return the JvmRoute for the specified context. + * + * @param context + * @return + */ + protected String getJvmRoute(Context context) { + return ((Engine) context.getParent().getParent()).getJvmRoute(); + } + + + /** + * Return the host and its alias list with which the context is associated. + * + * @param context + * @return + */ + protected String getHost(Context context) { + StringBuffer result = new StringBuffer(); + Host host = (Host) context.getParent(); + result.append(host.getName()); + String[] aliases = host.findAliases(); + for (int i = 0; i < aliases.length; i++) { + result.append(','); + result.append(aliases[i]); + } + return result.toString(); + } + + + /** + * Find the most likely connector the proxy server should connect to, or + * accept connections from. + * + * @param connectors + * @return + */ + protected Connector findProxyConnector(Connector[] connectors) { + int pos = 0; + int maxThreads = 0; + for (int i = 0; i < connectors.length; i++) { + if (connectors[i].getProtocol().startsWith("AJP")) { + // Return any AJP connector found + return connectors[i]; + } + if (Boolean.TRUE.equals(IntrospectionUtils.getProperty(connectors[i].getProtocolHandler(), "reverseConnection"))) { + return connectors[i]; + } + Integer mt = (Integer) IntrospectionUtils.getProperty(connectors[i].getProtocolHandler(), "maxThreads"); + if (mt.intValue() > maxThreads) { + maxThreads = mt.intValue(); + pos = i; + } + } + // If no AJP connector and no reverse, return the connector with the most threads + return connectors[pos]; + } + + + /** + * Return the address on which the connector is bound. + * + * @param connector + * @return + */ + protected String getAddress(Connector connector) { + InetAddress inetAddress = + (InetAddress) IntrospectionUtils.getProperty(connector.getProtocolHandler(), "address"); + if (inetAddress == null) { + // Should not happen + return "127.0.0.1"; + } else { + return inetAddress.getHostAddress(); + } + } + + + /** + * Send HTTP request, with the specified list of parameters. If an IO error occurs, the error state will + * be set. If the front end server reports an error, will mark as error state. Other unexpected exceptions + * will be thrown and the error state will be set. + * + * @param command + * @param wildcard + * @param parameters + * @return the response body as a String; null if in error state or a normal error occurs + */ + protected void sendRequest(String command, boolean wildcard, HashMap parameters) { + sendRequest(command, wildcard, parameters, -1); + } + + protected synchronized String sendRequest(String command, boolean wildcard, HashMap parameters, int pos) { + + BufferedReader reader = null; + BufferedWriter writer = null; + CharChunk body = null; + + // First, encode the POST body + try { + body = encoder.encodeURL("", 0, 0); + body.recycle(); + Iterator keys = parameters.keySet().iterator(); + while (keys.hasNext()) { + String key = keys.next(); + String value = parameters.get(key); + if (value == null) { + throw new IllegalArgumentException(sm.getString("clusterListener.error.nullAttribute", key)); + } + body = encoder.encodeURL(key, 0, key.length()); + body.append('='); + if (value != null) { + body = encoder.encodeURL(value, 0, value.length()); + } + if (keys.hasNext()) { + body.append('&'); + } + } + } catch (IOException e) { + body.recycle(); + // Error encoding URL, should not happen + throw new IllegalArgumentException(e); + } + + int start = 0; + int end = proxies.length; + if (pos != -1) { + start = pos; + end = pos + 1; + } + + for (int i = start; i < end; i++) { + + // If there was an error, do nothing until the next periodic event, where the whole configuration + // will be refreshed + if (proxies[i].state != State.OK) { + continue; + } + + if (log.isDebugEnabled()) { + log.debug(sm.getString("clusterListener.request", command, wildcard, proxies[i])); + } + + try { + + // Then, connect to the proxy + getConnection(i); + writer = getConnectionWriter(i); + // Check connection to see if it is still alive (not really allowed, + // but httpd deals with the extra CRLF) + try { + writer.write("\r\n"); + writer.flush(); + } catch (IOException e) { + // Get a new connection; if it fails this second time, it is an error + closeConnection(i); + getConnection(i); + writer = getConnectionWriter(i); + } + + // Generate and write request + String url = proxyURL; + if (url == null) { + url = (wildcard) ? "/*" : "/"; + } else { + if (url.endsWith("/") && wildcard) { + url = url + "*"; + } else if (wildcard) { + url = url + "/*"; + } + } + String requestLine = command + " " + url + " HTTP/1.0"; + writer.write(requestLine); + writer.write("\r\n"); + writer.write("Content-Length: " + body.getLength() + "\r\n"); + writer.write("User-Agent: ClusterListener/1.0\r\n"); + writer.write("Connection: Keep-Alive\r\n"); + writer.write("\r\n"); + writer.write(body.getBuffer(), body.getStart(), body.getLength()); + writer.write("\r\n"); + writer.flush(); + + // Read the response to a string + reader = getConnectionReader(i); + // Read the first response line and skip the rest of the HTTP header + String responseStatus = reader.readLine(); + // Parse the line, which is formed like HTTP/1.x YYY Message + int status = 500; + String version = "0"; + String message = null; + String errorType = null; + int contentLength = 0; + if (responseStatus != null) { + try { + responseStatus = responseStatus.substring(responseStatus.indexOf(' ') + 1, responseStatus.indexOf(' ', responseStatus.indexOf(' ') + 1)); + status = Integer.parseInt(responseStatus); + String header = reader.readLine(); + while (!"".equals(header)) { + int colon = header.indexOf(':'); + String headerName = header.substring(0, colon).trim(); + String headerValue = header.substring(colon + 1).trim(); + if ("version".equalsIgnoreCase(headerName)) { + version = headerValue; + } else if ("type".equalsIgnoreCase(headerName)) { + errorType = headerValue; + } else if ("mess".equalsIgnoreCase(headerName)) { + message = headerValue; + } else if ("content-length".equalsIgnoreCase(headerName)) { + contentLength = Integer.parseInt(headerValue); + } + header = reader.readLine(); + } + } catch (Exception e) { + log.info(sm.getString("clusterListener.error.parse", command), e); + } + } + + // Mark as error if the front end server did not return 200; the configuration will + // be refreshed during the next periodic event + if (status == 200) { + // Read the request body + StringBuffer result = new StringBuffer(); + if (contentLength > 0) { + int thisTime = contentLength; + char[] buf = new char[512]; + while (contentLength > 0) { + thisTime = (contentLength > buf.length) ? buf.length : contentLength; + int n = reader.read(buf, 0, thisTime); + if (n <= 0) { + break; + } else { + result.append(buf, 0, n); + contentLength -= n; + } + } + } + if (pos != -1) { + return result.toString(); + } + } else { + if ("SYNTAX".equals(errorType)) { + // Syntax error means the protocol is incorrect, which cannot be automatically fixed + proxies[i].state = State.DOWN; + log.error(sm.getString("clusterListener.error.syntax", command, proxies[i], errorType, message)); + } else { + proxies[i].state = State.ERROR; + log.error(sm.getString("clusterListener.error.other", command, proxies[i], errorType, message)); + } + } + + } catch (IOException e) { + // Most likely this is a connection error with the proxy + proxies[i].state = State.ERROR; + log.info(sm.getString("clusterListener.error.io", command, proxies[i]), e); + } finally { + // If there's an error of any sort, or if the proxy did not return 200, it is an error + if (proxies[i].state != State.OK) { + closeConnection(i); + } + } + + } + + return null; + + } + + + /** + * SSL init. + */ + protected void sslInit() { + if (ssl) { + sslSocketFactory = new JSSESocketFactory(this); + } + } + + + /** + * Return a reader to the proxy. + */ + protected Socket getConnection(int i) + throws IOException { + if (connections[i] == null) { + InetAddress address = (proxies[i].address == null) ? InetAddress.getLocalHost() : proxies[i].address; + if (ssl) { + connections[i] = sslSocketFactory.createSocket(address, proxies[i].port); + } else { + connections[i] = new Socket(address, proxies[i].port); + } + connections[i].setSoTimeout(socketTimeout); + } + return connections[i]; + } + + + /** + * Return a reader to the proxy. + */ + protected BufferedReader getConnectionReader(int i) + throws IOException { + if (connectionReaders[i] == null) { + connectionReaders[i] = new BufferedReader(new InputStreamReader(connections[i].getInputStream())); + } + return connectionReaders[i]; + } + + + /** + * Return a writer to the proxy. + */ + protected BufferedWriter getConnectionWriter(int i) + throws IOException { + if (connectionWriters[i] == null) { + connectionWriters[i] = new BufferedWriter(new OutputStreamWriter(connections[i].getOutputStream())); + } + return connectionWriters[i]; + } + + + /** + * Close connection. + */ + protected void closeConnection(int i) { + try { + if (connectionReaders[i] != null) { + connectionReaders[i].close(); + } + } catch (IOException e) { + // Ignore + } + connectionReaders[i] = null; + try { + if (connectionWriters[i] != null) { + connectionWriters[i].close(); + } + } catch (IOException e) { + // Ignore + } + connectionWriters[i] = null; + try { + if (connections[i] != null) { + connections[i].close(); + } + } catch (IOException e) { + // Ignore + } + connections[i] = null; + } + + + // ------------------------------------------------------ Proxy Inner Class + + + /** + * This class represents a front-end httpd server. + */ + protected static class Proxy { + public InetAddress address = null; + public int port = 8000; + public State state = State.OK; + + public String toString() { + if (address == null) { + return ":" + port; + } else { + return address.getHostAddress() + ":" + port; + } + } + + public boolean equals(Object o) { + if (o instanceof Proxy) { + Proxy compare = (Proxy) o; + if (port != compare.port) { + return false; + } + if (compare.address == null) { + if (address == null) { + return true; + } + } else if ((compare.address.equals(address)) && port == compare.port) { + return true; + } + } + return false; + } + + } + + +} Index: 3rdParty_sources/jbossweb/org/jboss/web/cluster/Constants.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/web/cluster/Constants.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/web/cluster/Constants.java 17 Aug 2012 14:43:39 -0000 1.1 @@ -0,0 +1,31 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2006, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + + +package org.jboss.web.cluster; + + +public class Constants { + + public static final String Package = "org.jboss.web.cluster"; + +} Index: 3rdParty_sources/jbossweb/org/jboss/web/cluster/JSSEKeyManager.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/web/cluster/JSSEKeyManager.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/web/cluster/JSSEKeyManager.java 17 Aug 2012 14:43:38 -0000 1.1 @@ -0,0 +1,144 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jboss.web.cluster; + +import java.net.Socket; +import java.security.Principal; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import javax.net.ssl.X509KeyManager; + +/** + * X509KeyManager which allows selection of a specific keypair and certificate + * chain (identified by their keystore alias name) to be used by the server to + * authenticate itself to SSL clients. + * + * @author Jan Luehe + */ +public final class JSSEKeyManager implements X509KeyManager { + + private X509KeyManager delegate; + private String serverKeyAlias; + + /** + * Constructor. + * + * @param mgr The X509KeyManager used as a delegate + * @param serverKeyAlias The alias name of the server's keypair and + * supporting certificate chain + */ + public JSSEKeyManager(X509KeyManager mgr, String serverKeyAlias) { + this.delegate = mgr; + this.serverKeyAlias = serverKeyAlias; + } + + /** + * Choose an alias to authenticate the client side of a secure socket, + * given the public key type and the list of certificate issuer authorities + * recognized by the peer (if any). + * + * @param keyType The key algorithm type name(s), ordered with the + * most-preferred key type first + * @param issuers The list of acceptable CA issuer subject names, or null + * if it does not matter which issuers are used + * @param socket The socket to be used for this connection. This parameter + * can be null, in which case this method will return the most generic + * alias to use + * + * @return The alias name for the desired key, or null if there are no + * matches + */ + public String chooseClientAlias(String[] keyType, Principal[] issuers, + Socket socket) { + return delegate.chooseClientAlias(keyType, issuers, socket); + } + + /** + * Returns this key manager's server key alias that was provided in the + * constructor. + * + * @param keyType The key algorithm type name (ignored) + * @param issuers The list of acceptable CA issuer subject names, or null + * if it does not matter which issuers are used (ignored) + * @param socket The socket to be used for this connection. This parameter + * can be null, in which case this method will return the most generic + * alias to use (ignored) + * + * @return Alias name for the desired key + */ + public String chooseServerAlias(String keyType, Principal[] issuers, + Socket socket) { + return serverKeyAlias; + } + + /** + * Returns the certificate chain associated with the given alias. + * + * @param alias The alias name + * + * @return Certificate chain (ordered with the user's certificate first + * and the root certificate authority last), or null if the alias can't be + * found + */ + public X509Certificate[] getCertificateChain(String alias) { + return delegate.getCertificateChain(alias); + } + + /** + * Get the matching aliases for authenticating the client side of a secure + * socket, given the public key type and the list of certificate issuer + * authorities recognized by the peer (if any). + * + * @param keyType The key algorithm type name + * @param issuers The list of acceptable CA issuer subject names, or null + * if it does not matter which issuers are used + * + * @return Array of the matching alias names, or null if there were no + * matches + */ + public String[] getClientAliases(String keyType, Principal[] issuers) { + return delegate.getClientAliases(keyType, issuers); + } + + /** + * Get the matching aliases for authenticating the server side of a secure + * socket, given the public key type and the list of certificate issuer + * authorities recognized by the peer (if any). + * + * @param keyType The key algorithm type name + * @param issuers The list of acceptable CA issuer subject names, or null + * if it does not matter which issuers are used + * + * @return Array of the matching alias names, or null if there were no + * matches + */ + public String[] getServerAliases(String keyType, Principal[] issuers) { + return delegate.getServerAliases(keyType, issuers); + } + + /** + * Returns the key associated with the given alias. + * + * @param alias The alias name + * + * @return The requested key, or null if the alias can't be found + */ + public PrivateKey getPrivateKey(String alias) { + return delegate.getPrivateKey(alias); + } +} Index: 3rdParty_sources/jbossweb/org/jboss/web/cluster/JSSESocketFactory.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/web/cluster/JSSESocketFactory.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/web/cluster/JSSESocketFactory.java 17 Aug 2012 14:43:38 -0000 1.1 @@ -0,0 +1,551 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jboss.web.cluster; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.InetAddress; +import java.net.Socket; +import java.security.KeyStore; +import java.security.SecureRandom; +import java.security.cert.CRL; +import java.security.cert.CRLException; +import java.security.cert.CertPathParameters; +import java.security.cert.CertStore; +import java.security.cert.CertStoreParameters; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.CollectionCertStoreParameters; +import java.security.cert.PKIXBuilderParameters; +import java.security.cert.X509CertSelector; +import java.util.Collection; +import java.util.Vector; + +import javax.net.ssl.CertPathTrustManagerParameters; +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.ManagerFactoryParameters; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509KeyManager; + +import org.apache.tomcat.util.res.StringManager; + +/* + 1. Make the JSSE's jars available, either as an installed + extension (copy them into jre/lib/ext) or by adding + them to the Tomcat classpath. + 2. keytool -genkey -alias tomcat -keyalg RSA + Use "changeit" as password ( this is the default we use ) + */ + +/** + * SSL server socket factory. It _requires_ a valid RSA key and + * JSSE. + * + * @author Harish Prabandham + * @author Costin Manolache + * @author Stefan Freyr Stefansson + * @author EKR -- renamed to JSSESocketFactory + * @author Jan Luehe + * @author Bill Barker + */ +public class JSSESocketFactory { + + private static StringManager sm = + StringManager.getManager("org.apache.tomcat.util.net.jsse.res"); + + static org.jboss.logging.Logger log = + org.jboss.logging.Logger.getLogger(JSSESocketFactory.class); + + protected boolean initialized; + //protected String clientAuth = "false"; + protected SSLSocketFactory sslProxy = null; + protected String[] enabledCiphers; + protected ClusterListener listener = null; + + /** + * Flag to state that we require client authentication. + */ + //protected boolean requireClientAuth = false; + + /** + * Flag to state that we would like client authentication. + */ + //protected boolean wantClientAuth = false; + + + public JSSESocketFactory (ClusterListener listener) { + this.listener = listener; + } + + public Socket createSocket (InetAddress ifAddress, int port) + throws IOException + { + if (!initialized) init(); + Socket socket = sslProxy.createSocket(ifAddress, port); + initSocket(socket); + return socket; + } + + public void handshake(Socket sock) throws IOException { + ((SSLSocket)sock).startHandshake(); + } + + /* + * Determines the SSL cipher suites to be enabled. + * + * @param requestedCiphers Comma-separated list of requested ciphers + * @param supportedCiphers Array of supported ciphers + * + * @return Array of SSL cipher suites to be enabled, or null if none of the + * requested ciphers are supported + */ + protected String[] getEnabledCiphers(String requestedCiphers, + String[] supportedCiphers) { + + String[] enabledCiphers = null; + + if (requestedCiphers != null) { + Vector vec = null; + String cipher = requestedCiphers; + int index = requestedCiphers.indexOf(','); + if (index != -1) { + int fromIndex = 0; + while (index != -1) { + cipher = requestedCiphers.substring(fromIndex, index).trim(); + if (cipher.length() > 0) { + /* + * Check to see if the requested cipher is among the + * supported ciphers, i.e., may be enabled + */ + for (int i=0; supportedCiphers != null + && i 0) { + /* + * Check to see if the requested cipher is among the + * supported ciphers, i.e., may be enabled + */ + for (int i=0; supportedCiphers != null + && iPKIX is supported. + * + * @param algorithm The algorithm to get parameters for. + * @param crlf The path to the CRL file. + * @param trustStore The configured TrustStore. + * @return The parameters including the CRLs and TrustStore. + */ + protected CertPathParameters getParameters(String algorithm, + String crlf, + KeyStore trustStore) + throws Exception { + CertPathParameters params = null; + if("PKIX".equalsIgnoreCase(algorithm)) { + PKIXBuilderParameters xparams = new PKIXBuilderParameters(trustStore, + new X509CertSelector()); + Collection crls = getCRLs(crlf); + CertStoreParameters csp = new CollectionCertStoreParameters(crls); + CertStore store = CertStore.getInstance("Collection", csp); + xparams.addCertStore(store); + xparams.setRevocationEnabled(true); + xparams.setMaxPathLength(listener.getSslTrustMaxCertLength()); + + params = xparams; + } else { + throw new CRLException("CRLs not supported for type: "+algorithm); + } + return params; + } + + + /** + * Load the collection of CRLs. + * + */ + protected Collection getCRLs(String crlf) + throws IOException, CRLException, CertificateException { + + File crlFile = new File(crlf); + if( !crlFile.isAbsolute() ) { + crlFile = new File(System.getProperty("catalina.base"), crlf); + } + Collection crls = null; + InputStream is = null; + try { + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + is = new FileInputStream(crlFile); + crls = cf.generateCRLs(is); + } catch(IOException iex) { + throw iex; + } catch(CRLException crle) { + throw crle; + } catch(CertificateException ce) { + throw ce; + } finally { + if(is != null) { + try{ + is.close(); + } catch(Exception ex) { + } + } + } + return crls; + } + + /** + * Set the SSL protocol variants to be enabled. + * @param socket the SSLServerSocket. + * @param protocols the protocols to use. + */ + protected void setEnabledProtocols(SSLSocket socket, String []protocols){ + if (protocols != null) { + socket.setEnabledProtocols(protocols); + } + } + + /** + * Determines the SSL protocol variants to be enabled. + * + * @param socket The socket to get supported list from. + * @param requestedProtocols Comma-separated list of requested SSL + * protocol variants + * + * @return Array of SSL protocol variants to be enabled, or null if none of + * the requested protocol variants are supported + */ + protected String[] getEnabledProtocols(SSLSocket socket, + String requestedProtocols){ + String[] supportedProtocols = socket.getSupportedProtocols(); + + String[] enabledProtocols = null; + + if (requestedProtocols != null) { + Vector vec = null; + String protocol = requestedProtocols; + int index = requestedProtocols.indexOf(','); + if (index != -1) { + int fromIndex = 0; + while (index != -1) { + protocol = requestedProtocols.substring(fromIndex, index).trim(); + if (protocol.length() > 0) { + /* + * Check to see if the requested protocol is among the + * supported protocols, i.e., may be enabled + */ + for (int i=0; supportedProtocols != null + && i 0) { + /* + * Check to see if the requested protocol is among the + * supported protocols, i.e., may be enabled + */ + for (int i=0; supportedProtocols != null + && i + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Index: 3rdParty_sources/jbossweb/org/jboss/web/cluster/advertise/AdvertiseEventType.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/web/cluster/advertise/AdvertiseEventType.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/web/cluster/advertise/AdvertiseEventType.java 17 Aug 2012 14:43:39 -0000 1.1 @@ -0,0 +1,62 @@ +/* + * + * Copyright(c) 2008 Red Hat Middleware, LLC, + * and individual contributors as indicated by the @authors tag. + * See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + */ + +package org.jboss.web.cluster.advertise; + +/** + * Set what type of event the AdvertiseEvent signals. + * @param type The type of event. One of: + *

+ * ON_NEW_SERVER     --  New AdvertisedServer detected
+ * ON_STATUS_CHANGE  --  AdvertisedServer server changed status
+ * 
+ */ +public enum AdvertiseEventType +{ + /** New AdvertisedServer detected */ + ON_NEW_SERVER( 0), + /** AdvertisedServer server changed status */ + ON_STATUS_CHANGE( 1); + + private int value; + private AdvertiseEventType(int v) + { + value = v; + } + + public int valueOf() + { + return value; + } + + public static AdvertiseEventType valueOf(int value) + { + for (AdvertiseEventType e : values()) { + if (e.value == value) + return e; + } + throw new IllegalArgumentException("Invalid initializer: " + value); + } + +} Index: 3rdParty_sources/jbossweb/org/jboss/web/cluster/advertise/AdvertiseListener.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/web/cluster/advertise/AdvertiseListener.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/web/cluster/advertise/AdvertiseListener.java 17 Aug 2012 14:43:39 -0000 1.1 @@ -0,0 +1,448 @@ +/* + * + * Copyright(c) 2008 Red Hat Middleware, LLC, + * and individual contributors as indicated by the @authors tag. + * See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + */ + +package org.jboss.web.cluster.advertise; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.InetAddress; +import java.net.MulticastSocket; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.Locale; + +import org.jboss.web.cluster.ClusterListener; + + +/** AdvertiseListener + * Listens for Advertise messages from mod_cluster + * + * @author Mladen Turk + * + */ +public class AdvertiseListener +{ + /** Default port for listening Advertise messages. + */ + public static int DEFAULT_PORT = 23364; + /** Default Multicast group address for listening Advertise messages. + */ + public static String DEFAULT_GROUP = "224.0.1.105"; + + private static String RFC_822_FMT = "EEE, d MMM yyyy HH:mm:ss Z"; + private int advertisePort = DEFAULT_PORT; + private String groupAddress = DEFAULT_GROUP; + private MulticastSocket ms; + private SimpleDateFormat df; + private boolean listening = true; + private boolean initialized = false; + private boolean running = false; + private boolean paused = false; + private boolean daemon = true; + private byte [] secure = new byte[16]; + private String securityKey = null; + private MessageDigest md = null; + + private HashMap servers; + + private ClusterListener listener; + private Thread workerThread; + + + private static void digestString(MessageDigest md, String s) + { + int len = s.length(); + byte [] b = new byte[len]; + for (int i = 0; i < len; i++) { + char c = s.charAt(i); + if (c < 127) + b[i] = (byte)c; + else + b[i] = '?'; + } + md.update(b); + } + + /** Create AdvertiseListener instance + * @param eventHandler The event handler that will be used for + * status and new server notifications. + */ + public AdvertiseListener(ClusterListener listener) + { + df = new SimpleDateFormat(RFC_822_FMT, Locale.US); + servers = new HashMap(); + this.listener = listener; + } + + /** + * The default is true - the control thread will be + * in daemon mode. If set to false, the control thread + * will not be daemon - and will keep the process alive. + */ + public void setDaemon(boolean b) + { + daemon = b; + } + + public boolean getDaemon() + { + return daemon; + } + + /** Set Advertise security key + * @param key The key to use.
+ * Security key must match the AdvertiseKey + * on the advertised server. + */ + public void setSecurityKey(String key) + throws NoSuchAlgorithmException + { + securityKey = key; + if (md == null) + md = MessageDigest.getInstance("MD5"); + } + + /** Set Advertise port + * @param port The UDP port to use. + */ + public void setAdvertisePort(int port) + { + advertisePort = port; + } + + public int getAdvertisePort() + { + return advertisePort; + } + + /** Set Advertise Multicaset group address + * @param address The IP or host address to use. + */ + public void setGroupAddress(String address) + { + groupAddress = address; + } + + /** Get Advertise Multicaset group address + */ + public String getGroupAddress() + { + return groupAddress; + } + + /** Get Collection of all AdvertisedServer instances. + */ + public Collection getServers() + { + return servers.values(); + } + + /** Get AdvertiseServer server. + * @param name Server name to get. + */ + public AdvertisedServer getServer(String name) + { + return servers.get(name); + } + + /** Remove the AdvertisedServer from the collection. + * @param server Server to remove. + */ + public void removeServer(AdvertisedServer server) + { + servers.remove(server); + } + + private void init() + throws IOException + { + ms = new MulticastSocket(advertisePort); + ms.setTimeToLive(16); + ms.joinGroup(InetAddress.getByName(groupAddress)); + initialized = true; + } + + private void interruptDatagramReader() + { + if (!initialized) + return; + try { + // Restrict to localhost. + ms.setTimeToLive(0); + DatagramPacket dp = new DatagramPacket(secure, secure.length, + InetAddress.getByName(groupAddress), + advertisePort); + ms.send(dp); + } catch (IOException e) { + // Ignore + } + } + + /** Start the Listener, creating listener thread. + */ + public void start() + throws IOException + { + if (!initialized) { + init(); + } + if (!running) { + SecureRandom random = new SecureRandom(); + random.nextBytes(secure); + secure[0] = 0; + running = true; + paused = false; + listening = true; + AdvertiseListenerWorker aw = new AdvertiseListenerWorker(); + workerThread = new Thread(aw); + workerThread.setDaemon(daemon); + workerThread.start(); + } + } + + /** + * Pause the listener, which will make it stop accepting new advertise + * messages. + */ + public void pause() + { + if (running && !paused) { + paused = true; + interruptDatagramReader(); + } + } + + + /** + * Resume the listener, which will make it start accepting new advertise + * messages again. + */ + public void resume() + { + if (running && paused) { + // Genererate new private secure + SecureRandom random = new SecureRandom(); + random.nextBytes(secure); + secure[0] = 0; + paused = false; + } + } + + + /** + * Stop the endpoint. This will cause all processing threads to stop. + */ + public void stop() + { + if (running) { + running = false; + interruptDatagramReader(); + workerThread = null; + } + } + + + /** + * Deallocate listener and close sockets. + */ + public void destroy() + throws IOException + { + if (running) { + stop(); + } + if (initialized) { + ms.leaveGroup(InetAddress.getByName(groupAddress)); + ms.close(); + initialized = false; + ms = null; + } + } + + private boolean verifyDigest(String digest, String server, String date) + { + if (md == null) + return true; + md.reset(); + digestString(md, securityKey); + digestString(md, date); + digestString(md, server); + byte [] our = md.digest(); + byte [] dst = new byte[digest.length() * 2]; + for (int i = 0, j = 0; i < digest.length(); i++) { + char ch = digest.charAt(i); + dst[j++] = (byte)((ch >= 'A') ? ((ch & 0xdf) - 'A') + 10 : (ch - '0')); + } + return true; + } + + /** + * True if listener is accepting the advetise messages.
+ * If false it means that listener is experiencing some + * network problems if running. + */ + public boolean isListening() + { + return listening; + } + + + // ------------------------------------ AdvertiseListenerWorker Inner Class + private class AdvertiseListenerWorker implements Runnable + { + + protected AdvertiseListenerWorker() + { + // Nothing + } + /** + * The background thread that listens for incoming Advertise packets + * and hands them off to an appropriate AdvertiseEvent handler. + */ + public void run() { + byte[] buffer = new byte[512]; + // Loop until we receive a shutdown command + while (running) { + // Loop if endpoint is paused + while (paused) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + // Ignore + } + } + try { + DatagramPacket dp = new DatagramPacket(buffer, buffer.length); + ms.receive(dp); + if (!running) + break; + byte [] data = dp.getData(); + boolean intr = false; + if (dp.getLength() == secure.length) { + int i; + for (i = 0; i < secure.length; i++) { + if (data[i] != secure[i]) + break; + } + if (i == secure.length) + intr = true; + } + if (intr) + continue; + String s = new String(data, 0, dp.getLength(), "8859_1"); + if (!s.startsWith("HTTP/1.")) + continue; + + String [] headers = s.split("\r\n"); + String date_str = null; + Date date = null; + int status = 0; + String status_desc = null; + String digest = null; + String server_name = null; + AdvertisedServer server = null; + boolean added = false; + for (int i = 0; i < headers.length; i++) { + if (i == 0) { + String [] sline = headers[i].split(" ", 3); + if (sline == null || sline.length != 3) + break; + status = Integer.parseInt(sline[1]); + if (status < 100) + break; + status_desc = sline[2]; + } + else { + String [] hdrv = headers[i].split(": ", 2); + if (hdrv == null || hdrv.length != 2) + break; + if (hdrv[0].equals("Date")) { + date_str = hdrv[1]; + try { + date = df.parse(date_str); + } catch (ParseException e) { + date = new Date(); + } + } + else if (hdrv[0].equals("Digest")) { + digest = hdrv[1]; + } + else if (hdrv[0].equals("Server")) { + server_name = hdrv[1]; + server = servers.get(server_name); + if (server == null) { + server = new AdvertisedServer(server_name); + added = true; + } + } + else if (server != null) { + server.setParameter(hdrv[0], hdrv[1]); + } + } + } + if (server != null && status > 0) { + if (md != null) { + /* We need a digest to match */ + if (!verifyDigest(digest, server_name, date_str)) { + System.out.println("Digest mismatch"); + continue; + } + } + server.setDate(date); + boolean rc = server.setStatus(status, status_desc); + if (added) { + servers.put(server_name, server); + // Call the new server callback + //eventHandler.onEvent(AdvertiseEventType.ON_NEW_SERVER, server); + String proxy = server.getParameter(AdvertisedServer.MANAGER_ADDRESS); + if (proxy != null) { + listener.addProxy(proxy); + } + } + else if (rc) { + // Call the status change callback + //eventHandler.onEvent(AdvertiseEventType.ON_STATUS_CHANGE, server); + } + } + listening = true; + } catch (IOException e) { + // Do not blow the CPU in case of communication error + listening = false; + try { + Thread.sleep(100); + } catch (InterruptedException x) { + // Ignore + } + } + } + } + } + +} Index: 3rdParty_sources/jbossweb/org/jboss/web/cluster/advertise/AdvertisedServer.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/web/cluster/advertise/AdvertisedServer.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/web/cluster/advertise/AdvertisedServer.java 17 Aug 2012 14:43:39 -0000 1.1 @@ -0,0 +1,122 @@ +/* + * + * Copyright(c) 2008 Red Hat Middleware, LLC, + * and individual contributors as indicated by the @authors tag. + * See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + */ + +package org.jboss.web.cluster.advertise; + +import java.util.Date; +import java.util.HashMap; + +/** + * Advertised server instance + * + * @author Mladen Turk + * + */ +public class AdvertisedServer +{ + private String server; + private Date date; + private int status; + private String status_desc; + private HashMap headers; + + /** Manager-Address header */ + public static String MANAGER_ADDRESS = "X-Manager-Address"; + /** Manager-Url header */ + public static String MANAGER_URL = "X-Manager-Url"; + /** Manager-Protocol header */ + public static String MANAGER_PROTOCOL = "X-Manager-Protocol"; + /** Manager-Version header */ + public static String MANAGER_VERSION = "X-Manager-Version"; + /** Manager-Host header */ + public static String MANAGER_HOST = "X-Manager-Host"; + + protected AdvertisedServer(String server) + { + this.server = server; + headers = new HashMap(); + } + + protected boolean setStatus(int status, String desc) + { + boolean rv = false; + status_desc = desc; + if (this.status == 0 ) { + // First time + this.status = status; + } + else if (this.status != status) { + this.status = status; + rv = true; + } + return rv; + } + + /** Set the Date of the last Advertise message + */ + protected void setDate(Date date) + { + this.date = date; + } + + /** Set the Header + */ + protected void setParameter(String name, String value) + { + headers.put(name, value); + } + + /** Get Date of the last Advertise message + */ + public Date getDate() + { + return date; + } + + /** Get Status code of the last Advertise message + */ + public int getStatusCode() + { + return status; + } + + /** Get Status description of the last Advertise message + */ + public String getStatusDescription() + { + return status_desc; + } + + /** Get Advertise parameter + */ + public String getParameter(String name) + { + return headers.get(name); + } + + public String toString() + { + return server; + } +} Index: 3rdParty_sources/jbossweb/org/jboss/web/php/Constants.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/web/php/Constants.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/web/php/Constants.java 17 Aug 2012 14:43:39 -0000 1.1 @@ -0,0 +1,37 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2006, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.jboss.web.php; + + +/** + * Package Constants + * + * @author Mladen Turk + * @version $Revision: 1.1 $, $Date: 2012/08/17 14:43:39 $ + * @since 1.0 + */ + public class Constants { + + public static final String Package = "org.jboss.web.php"; + +} Index: 3rdParty_sources/jbossweb/org/jboss/web/php/Handler.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/web/php/Handler.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/web/php/Handler.java 17 Aug 2012 14:43:40 -0000 1.1 @@ -0,0 +1,219 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2006, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.jboss.web.php; + +import java.io.IOException; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.UnavailableException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.catalina.Globals; +import org.apache.catalina.util.StringManager; + +/** + * Handler. + * + * @author Mladen Turk + * @version $Revision: 1.1 $, $Date: 2012/08/17 14:43:40 $ + * @since 1.0 + */ +public class Handler extends HttpServlet +{ + + /** the debugging detail level for this servlet. */ + private int debug = 0; + + /** Buffer size. */ + private int bufferSize = 4096; + + /** + * The Servlet configuration object we are associated with. If this value + * is null, this filter instance is not currently configured. + */ + private ServletConfig servletConfig = null; + + /** + * The string manager for this package. + */ + private StringManager sm = + StringManager.getManager(Constants.Package); + + + /** Are doing source sysntax highlight. */ + protected boolean syntaxHighlight = false; + + /** the encoding to use for parameters */ + private String parameterEncoding = System.getProperty("file.encoding", + "UTF-8"); + + /** + * The Script search path will start at + * webAppRootDir + File.separator + scriptPathPrefix + * (or webAppRootDir alone if scriptPathPrefix is + * null) + */ + private String scriptPathPrefix = null; + + /** + * Sets instance variables. + *

+ * Modified from Craig R. McClanahan's InvokerServlet + *

+ * + * @param config a ServletConfig object + * containing the servlet's + * configuration and initialization + * parameters + * + * @exception ServletException if an exception has occurred that + * interferes with the servlet's normal + * operation + */ + public void init(ServletConfig servletConfig) + throws ServletException + { + super.init(servletConfig); + + if (!Library.isInitialized()) { + // try to load the library. + try { + Library.initialize(null); + } catch(Exception e) { + e.printStackTrace(); + } + } + + if (!Library.isInitialized()) + throw new UnavailableException + (sm.getString("handler.missing")); + + this.servletConfig = servletConfig; + + // Verify that we were not accessed using the invoker servlet + String servletName = servletConfig.getServletName(); + if (servletName == null) + servletName = ""; + if (servletName.startsWith("org.apache.catalina.INVOKER.")) + throw new UnavailableException + ("Cannot invoke Handler through the invoker"); + + + // Set our properties from the initialization parameters + String value = null; + try { + value = servletConfig.getInitParameter("debug"); + debug = Integer.parseInt(value); + scriptPathPrefix = + servletConfig.getInitParameter("scriptPathPrefix"); + value = servletConfig.getInitParameter("bufferSize"); + if (value != null) { + bufferSize = Integer.parseInt(value); + if (bufferSize < 1024) + bufferSize = 1024; + log("init: bufferSize set to " + bufferSize); + } + } catch (Throwable t) { + // Nothing. + } + log("init: loglevel set to " + debug); + + value = servletConfig.getInitParameter("parameterEncoding"); + if (value != null) { + parameterEncoding = value; + } + } + + /** + * Finalize this servlet. + */ + public void destroy() + { + this.servletConfig = null; + } + + private static native int php(byte[] buf, + ScriptEnvironment env, + HttpServletRequest req, + HttpServletResponse res, + String requestMethod, + String queryString, + String contentType, + String authUser, + String requestURI, + String pathTranslated, + int contentLength, + boolean syntaxHighlight); + + /** + * Provides PHP Gateway service + * + * @param req HttpServletRequest passed in by servlet container + * @param res HttpServletResponse passed in by servlet container + * + * @exception ServletException if a servlet-specific exception occurs + * @exception IOException if a read/write exception occurs + * + * @see javax.servlet.http.HttpServlet + * + */ + protected void service(HttpServletRequest req, HttpServletResponse res) + throws ServletException, IOException + { + + // Verify that we were not accessed using the invoker servlet + if (req.getAttribute(Globals.INVOKED_ATTR) != null) + throw new UnavailableException + ("Cannot invoke PHP Gateway Handler through the invoker"); + + ScriptEnvironment env = new ScriptEnvironment(req, + getServletContext(), + scriptPathPrefix); + if (env.isValid()) { + byte[] buf = new byte[bufferSize]; + int rv = php(buf, + env, + req, + res, + req.getMethod(), + req.getQueryString(), + req.getContentType(), + req.getRemoteUser(), + req.getRequestURI(), + env.getFullPath(), + req.getContentLength(), + syntaxHighlight); + } + else { + res.setStatus(HttpServletResponse.SC_NOT_FOUND); + } + } + + public static void log(Handler handler, String msg) + { + // TODO: Log the message + } +} Index: 3rdParty_sources/jbossweb/org/jboss/web/php/Highlight.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/web/php/Highlight.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/web/php/Highlight.java 17 Aug 2012 14:43:40 -0000 1.1 @@ -0,0 +1,58 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2006, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.jboss.web.php; + +import java.io.IOException; + +import javax.servlet.*; +import javax.servlet.http.*; + +/** + * Highlight. + * + * @author Mladen Turk + * @version $Revision: 1.1 $, $Date: 2012/08/17 14:43:40 $ + * @since 1.0 + */ +public class Highlight extends Handler +{ + + /** + * Provides PHP Highlight Gateway service + * + * @param req HttpServletRequest passed in by servlet container + * @param res HttpServletResponse passed in by servlet container + * + * @exception ServletException if a servlet-specific exception occurs + * @exception IOException if a read/write exception occurs + * + * @see org.apache.catalina.servlets.php.Handler + * + */ + protected void service(HttpServletRequest req, HttpServletResponse res) + throws ServletException, IOException + { + syntaxHighlight = true; + super.service(req, res); + } +} Index: 3rdParty_sources/jbossweb/org/jboss/web/php/Library.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/web/php/Library.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/web/php/Library.java 17 Aug 2012 14:43:39 -0000 1.1 @@ -0,0 +1,146 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2006, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.jboss.web.php; + +/** + * Library class. + * + * @author Mladen Turk + * @version $Revision: 1.1 $, $Date: 2012/08/17 14:43:39 $ + * @since 1.0 + */ +public class Library { + + /* Default library names */ + private static String [] NAMES = { "php5servlet", "libphp5servlet" }; + + /* + * A handle to the unique Library singleton instance. + */ + private static Library engine = null; + private static boolean inited = false; + + static PhpThread phpthread = null; + + private Library() + { + boolean loaded = false; + String err = ""; + for (int i = 0; i < NAMES.length; i++) { + try { + System.loadLibrary(NAMES[i]); + loaded = true; + } catch (Throwable e) { + if ( i > 0) + err += ", "; + err += e.getMessage(); + } + if (loaded) + break; + } + if (!loaded) { + err += "("; + err += System.getProperty("java.library.path"); + err += ")"; + throw new UnsatisfiedLinkError(err); + } + } + + private Library(String libraryName) + { + System.loadLibrary(libraryName); + } + + /* PHP_MAJOR_VERSION */ + public static int PHP_MAJOR_VERSION = 0; + /* PHP_MINOR_VERSION */ + public static int PHP_MINOR_VERSION = 0; + /* PHP_PATCH_VERSION */ + public static int PHP_PATCH_VERSION = 0; + + /* Initialize PHP Engine + * This has to be the first call to PHP Module. + */ + public static native boolean startup(); + + /* destroy global PHP Engine + * This has to be the last call to PHP Module. + */ + public static native void shutdown(); + + /* Internal function for obtaining PHP Module version */ + private static native int version(int index); + + /** + * Setup any PHP internal data structures. This MUST be the + * first function called for PHP module. + * @param libraryName the name of the library to load + */ + public static boolean initialize(String libraryName) + throws Exception + { + if (engine == null) { + if (libraryName == null) + engine = new Library(); + else + engine = new Library(libraryName); + PHP_MAJOR_VERSION = version(1); + PHP_MINOR_VERSION = version(2); + PHP_PATCH_VERSION = version(3); + } + + phpthread = new PhpThread(); + phpthread.setDaemon(true); + phpthread.start(); + // Wait until the startup is done. + while (!inited && phpthread.isAlive()) { + Thread.currentThread().sleep(3000); + } + return inited; + } + + /** + * Check if PHP module is initialized. + */ + public static boolean isInitialized() + { + return inited; + } + + /** + * Terminate PHP Engine. + */ + public static void terminate() + { + if (engine != null) { + if (phpthread != null && phpthread.isAlive()) { + phpthread.interrupt(); + } + engine = null; + inited = false; + } + } + public static void StartUp() { + inited = startup(); + } +} Index: 3rdParty_sources/jbossweb/org/jboss/web/php/LifecycleListener.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/web/php/LifecycleListener.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/web/php/LifecycleListener.java 17 Aug 2012 14:43:39 -0000 1.1 @@ -0,0 +1,125 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2006, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.jboss.web.php; + +import java.lang.reflect.Method; +import org.apache.catalina.Lifecycle; +import org.apache.catalina.LifecycleEvent; +import org.apache.catalina.util.StringManager; +import org.jboss.logging.Logger; +import org.jboss.logging.Logger; + +/** + * Implementation of LifecycleListener that will init and + * and destroy PHP. + * + * @author Mladen Turk + * @version $Revision: 1.1 $ $Date: 2012/08/17 14:43:39 $ + * @since 1.0 + */ + +public class LifecycleListener + implements org.apache.catalina.LifecycleListener { + + private static Logger log = Logger.getLogger(LifecycleListener.class); + + /** + * The string manager for this package. + */ + protected StringManager sm = + StringManager.getManager(Constants.Package); + + // -------------------------------------------------------------- Constants + + + protected static final int REQUIRED_MAJOR = 5; + protected static final int REQUIRED_MINOR = 2; + protected static final int REQUIRED_PATCH = 3; + + + // ---------------------------------------------- LifecycleListener Methods + + + /** + * Primary entry point for startup and shutdown events. + * + * @param event The event that has occurred + */ + public void lifecycleEvent(LifecycleEvent event) { + + if (Lifecycle.INIT_EVENT.equals(event.getType())) { + int major = 0; + int minor = 0; + int patch = 0; + try { + String methodName = "initialize"; + Class paramTypes[] = new Class[1]; + paramTypes[0] = String.class; + Object paramValues[] = new Object[1]; + paramValues[0] = null; + Class clazz = Class.forName("org.jboss.web.php.Library"); + Method method = clazz.getMethod(methodName, paramTypes); + // TODO: Use sm to obtain optional library name. + method.invoke(null, paramValues); + major = clazz.getField("PHP_MAJOR_VERSION").getInt(null); + minor = clazz.getField("PHP_MINOR_VERSION").getInt(null); + patch = clazz.getField("PHP_PATCH_VERSION").getInt(null); + } catch (Throwable t) { + if (!log.isDebugEnabled()) { + log.info(sm.getString("listener.initialize", + System.getProperty("java.library.path"))); + } + else { + log.debug(sm.getString("listener.initialize", + System.getProperty("java.library.path")), t); + } + return; + } + // Check if the PHP Native module matches required version. + if ((major != REQUIRED_MAJOR) || + (minor != REQUIRED_MINOR) || + (patch < REQUIRED_PATCH)) { + log.error(sm.getString("listener.invalid", major + "." + + minor + "." + patch, REQUIRED_MAJOR + "." + + REQUIRED_MINOR + "." + + REQUIRED_PATCH)); + } + } + else if (Lifecycle.AFTER_STOP_EVENT.equals(event.getType())) { + try { + String methodName = "terminate"; + Method method = Class.forName("org.jboss.php.servlets.php.Library") + .getMethod(methodName, (Class [])null); + method.invoke(null, (Object []) null); + } + catch (Throwable t) { + if (!log.isDebugEnabled()) { + log.info(sm.getString("listener.terminate")); + } + else { + log.debug(sm.getString("listener.terminate"), t); + } + } + } + } +} Index: 3rdParty_sources/jbossweb/org/jboss/web/php/LocalStrings.properties =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/web/php/LocalStrings.properties,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/web/php/LocalStrings.properties 17 Aug 2012 14:43:39 -0000 1.1 @@ -0,0 +1,4 @@ +listener.initialize=The PHP Native library which allows executing PHP scripts was not found on the java.library.path: {0} +listener.invalid=An incompatible version {0} of the PHP Native library is installed, while Tomcat requires version {1} +listener.terminate=Failed shutdown of PHP Module +handler.missing=The PHP Native library which allows executing PHP scripts was not loaded Index: 3rdParty_sources/jbossweb/org/jboss/web/php/PhpThread.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/web/php/PhpThread.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/web/php/PhpThread.java 17 Aug 2012 14:43:39 -0000 1.1 @@ -0,0 +1,44 @@ +/* + * Copyright(c) 2007 Red Hat Middleware, LLC, + * and individual contributors as indicated by the @authors tag. + * See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * @author Jean-Frederic Clere + * @version $Revision: 1.1 $, $Date: 2012/08/17 14:43:39 $ + */ +package org.jboss.web.php; + +import java.lang.Thread; +import java.lang.InterruptedException; + +/* Thread that handle the php startup and shutdown */ +public class PhpThread extends Thread { + public void run() { + boolean ok = true; + Library.StartUp(); + while (ok) { + try { + sleep(60000); + } catch (InterruptedException e) { + ok = false; + } + } + Library.shutdown(); + } +} Index: 3rdParty_sources/jbossweb/org/jboss/web/php/SAPI.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/web/php/SAPI.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/web/php/SAPI.java 17 Aug 2012 14:43:39 -0000 1.1 @@ -0,0 +1,116 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2006, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.jboss.web.php; + +import java.io.IOException; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + + +/** + * PHP SAPI interface. + * + * @author Mladen Turk + * @version $Revision: 1.1 $, $Date: 2012/08/17 14:43:39 $ + * @since 1.0 + */ +public final class SAPI +{ + + public static int write(HttpServletResponse res, + byte[] buf, int len) + { + try { + res.getOutputStream().write(buf, 0, len); + return len; + } catch (IOException e) { + return -1; + } + } + + public static int read(HttpServletRequest req, + byte[] buf, int len) + { + try { + return req.getInputStream().read(buf, 0, len); + } catch (IOException e) { + return -1; + } + } + + public static void log(Handler h, String msg) + { + h.log("php: " + msg); + } + + public static int flush(HttpServletResponse res) + { + try { + res.getOutputStream().flush(); + return 0; + } catch (IOException e) { + return -1; + } + } + + public static void header(boolean set, + HttpServletResponse res, + String name, String value) + { + if (name.equalsIgnoreCase("Content-type")) { + res.setContentType(value); + } + else if (name.equalsIgnoreCase("Location")) { + try { + res.sendRedirect(value); + } catch (IOException e) { + // Nothing. + } + } + else { + if (set) + res.setHeader(name, value); + else + res.addHeader(name, value); + } + } + + public static void status(HttpServletResponse res, + int sc) + { + res.setStatus(sc); + } + + public static String[] env(ScriptEnvironment e) + { + return e.getEnvironmentArray(); + } + + public static String cookies(ScriptEnvironment e) + { + return (String)e.getEnvironment().get("HTTP_COOKIE"); + } + + +} Index: 3rdParty_sources/jbossweb/org/jboss/web/php/ScriptEnvironment.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/web/php/ScriptEnvironment.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/web/php/ScriptEnvironment.java 17 Aug 2012 14:43:39 -0000 1.1 @@ -0,0 +1,742 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2006, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.jboss.web.php; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.StringTokenizer; +import java.util.Vector; + +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; + +import org.apache.catalina.Globals; +import org.apache.catalina.util.IOTools; +import org.jboss.logging.Logger; +import org.jboss.logging.Logger; + + +/** + * Encapsulates the Script Environment and rules to derive + * that environment from the servlet container and request information. + * + * @author Mladen Turk + * @version $Revision: 1.1 $, $Date: 2012/08/17 14:43:39 $ + * @since 1.0 + */ +public class ScriptEnvironment { + + private static Logger log = Logger.getLogger(ScriptEnvironment.class); + + /** + * The Request attribute key for the client certificate chain. + */ + private static final String CERTIFICATE_KEY = "javax.servlet.request.X509Certificate"; + private static final String CIPHER_SUITE = "javax.servlet.request.cipher_suite"; + private static final String SSL_SESSION = "javax.servlet.request.ssl_session"; + private static final String KEY_SIZE = "javax.servlet.request.key_size"; + + /** context of the enclosing servlet */ + private ServletContext context = null; + + /** full path to the script file */ + private String scriptFullPath = null; + + /** context path of enclosing servlet */ + private String contextPath = null; + + /** servlet URI of the enclosing servlet */ + private String servletPath = null; + + /** pathInfo for the current request */ + private String pathInfo = null; + + /** real file system directory of the enclosing servlet's web app */ + private String webAppRootDir = null; + + /** tempdir for context - used to expand scripts in unexpanded wars */ + private File tempDir = null; + + /** derived script environment */ + private Hashtable env = null; + + /** script's desired working directory */ + private File workingDirectory = null; + + /** script's desired working directory */ + private File scriptFile = null; + + /** query parameters */ + private ArrayList queryParameters = new ArrayList(); + + /** whether or not this object is valid or not */ + private boolean valid = false; + + /** object used to ensure multiple threads don't try to expand same file */ + private static Object expandFileLock = new Object(); + + /** + * The Script search path will start at + * webAppRootDir + File.separator + scriptPathPrefix + * (or webAppRootDir alone if scriptPathPrefix is + * null) + */ + private String scriptPathPrefix = null; + + /** + * Resolves core information about the php script. + * + *

+ * Example URI: + *

 /servlet/scriptGateway/dir1/realScript/pathinfo1 
+ *
    + *
  • path = $CATALINA_HOME/mywebapp/dir1/realScript + *
  • scriptName = /servlet/scriptGateway/dir1/realScript + *
  • fullName = /dir1/realScript + *
  • name = realScript + *
+ *

+ *

+ * Script search algorithm: search the real path below + * <my-webapp-root> and find the first non-directory in + * the getPathTranslated("/"), reading/searching from left-to-right. + *

+ *

+ * The Script search path will start at + * webAppRootDir + File.separator + scriptPathPrefix + * (or webAppRootDir alone if scriptPathPrefix is + * null). + *

+ *

+ * scriptPathPrefix is defined by setting + * this servlet's scriptPathPrefix init parameter + * + *

+ * + * @param pathInfo String from HttpServletRequest.getPathInfo() + * @param webAppRootDir String from context.getRealPath("/") + * @param contextPath String as from + * HttpServletRequest.getContextPath() + * @param servletPath String as from + * HttpServletRequest.getServletPath() + * @param scriptPathPrefix Subdirectory of webAppRootDir below which + * the web app's Scripts may be stored; can be null. + * The Script search path will start at + * webAppRootDir + File.separator + scriptPathPrefix + * (or webAppRootDir alone if scriptPathPrefix is + * null). scriptPathPrefix is defined by setting + * the servlet's scriptPathPrefix init parameter. + * + * + * @return + *
    + *
  • + * path - full file-system path to valid script file, + * or null if no script file was found + *
  • + * scriptName - + * Script variable SCRIPT_NAME; the full URL path + * to valid script file or null if no script was + * found + *
  • + * fullName - servlet pathInfo fragment corresponding to + * the script itself, or null if not found + *
  • + * name - simple name (no directories) of the + * script, or null if no script was found + *
+ * + */ + protected String[] findScript(String pathInfo, String webAppRootDir, + String contextPath, String servletPath, + String scriptPathPrefix) + { + String path = null; + String name = null; + String scriptName = null; + String fullName = null; + + if ((webAppRootDir != null) + && (webAppRootDir.lastIndexOf(File.separator) == + (webAppRootDir.length() - 1))) { + //strip the trailing "/" from the webAppRootDir + webAppRootDir = + webAppRootDir.substring(0, (webAppRootDir.length() - 1)); + } + + if (scriptPathPrefix != null) { + webAppRootDir = webAppRootDir + File.separator + + scriptPathPrefix; + } + File currentLocation = new File(webAppRootDir); + StringTokenizer dirWalker = new StringTokenizer(pathInfo, "/"); + + while (!currentLocation.isFile() && dirWalker.hasMoreElements()) { + currentLocation = new File(currentLocation, + (String)dirWalker.nextElement()); + } + if (!currentLocation.isFile()) { + return new String[] { null, null, null, null }; + } + else { + + path = currentLocation.getAbsolutePath(); + name = currentLocation.getName(); + fullName = + currentLocation.getParent().substring(webAppRootDir.length()) + + File.separator + name; + // NOTE: Original CGI messes the Win path. + fullName = fullName.replace(File.separatorChar, '/'); + + if (contextPath != null && ! "".equals(contextPath) && ! "/".equals(contextPath)) { + scriptName = contextPath + fullName; + } + else { + // NOTE: set scriptName to fullName + scriptName = fullName; + } + + } + + return new String[] { path, scriptName, fullName, name }; + } + + /** + * Extracts requested resource from web app archive to context work + * directory to enable script to be executed. + */ + protected void expandScript() + { + StringBuffer srcPath = new StringBuffer(); + StringBuffer dstPath = new StringBuffer(); + InputStream is = null; + + // paths depend on mapping + if (scriptPathPrefix == null) { + srcPath.append(pathInfo); + is = context.getResourceAsStream(srcPath.toString()); + dstPath.append(tempDir); + dstPath.append(pathInfo); + } + else { + // essentially same search algorithm as findScript() + srcPath.append(scriptPathPrefix); + StringTokenizer dirWalker = new StringTokenizer(pathInfo, "/"); + // start with first element + while (dirWalker.hasMoreElements() && (is == null)) { + srcPath.append("/"); + srcPath.append(dirWalker.nextElement()); + is = context.getResourceAsStream(srcPath.toString()); + } + dstPath.append(tempDir); + dstPath.append("/"); + dstPath.append(srcPath); + } + + if (is == null) { + // didn't find anything, give up now + return; + } + + File f = new File(dstPath.toString()); + if (f.exists()) { + // Don't need to expand if it already exists + return; + } + + // create directories + String dirPath = new String(dstPath.toString().substring( 0, + dstPath.toString().lastIndexOf("/"))); + File dir = new File(dirPath); + dir.mkdirs(); + + try { + synchronized (expandFileLock) { + // make sure file doesn't exist + if (f.exists()) { + return; + } + + // create file + if (!f.createNewFile()) { + return; + } + FileOutputStream fos = new FileOutputStream(f); + + // copy data + IOTools.flow(is, fos); + is.close(); + fos.close(); + } + } catch (IOException ioe) { + // delete in case file is corrupted + if (f.exists()) { + f.delete(); + } + } + } + + /** + * Constructs the CGI environment to be supplied to the invoked CGI + * script; relies heavliy on Servlet API methods and findCGI + * + * @param req request associated with the CGI + * invokation + * + * @return true if environment was set OK, false if there + * was a problem and no environment was set + */ + protected boolean setEnvironment(HttpServletRequest req) + throws IOException + { + + /* + * This method is slightly ugly; c'est la vie. + * "You cannot stop [ugliness], you can only hope to contain [it]" + * (apologies to Marv Albert regarding MJ) + */ + + Hashtable envp = new Hashtable(); + + // Add the shell environment variables (if any) + // envp.putAll(shellEnv); + + // Add the Script environment variables + String sPathInfoOrig = null; + String sPathTranslatedOrig = null; + String sPathInfo = null; + String sPathTranslated = null; + String sFullPath = null; + String sScriptName = null; + String sFullName = null; + String sName = null; + String[] sScriptNames; + + + sPathInfoOrig = this.pathInfo; + sPathInfoOrig = sPathInfoOrig == null ? "" : sPathInfoOrig; + + sPathTranslatedOrig = req.getPathTranslated(); + sPathTranslatedOrig = + sPathTranslatedOrig == null ? "" : sPathTranslatedOrig; + + if (webAppRootDir == null ) { + // The app has not been deployed in exploded form + webAppRootDir = tempDir.toString(); + expandScript(); + } + + sScriptNames = findScript(sPathInfoOrig, + webAppRootDir, + contextPath, + servletPath, + scriptPathPrefix); + + sFullPath = sScriptNames[0]; + sScriptName = sScriptNames[1]; + sFullName = sScriptNames[2]; + sName = sScriptNames[3]; + + if (sFullPath == null + || sScriptName == null + || sFullName == null + || sName == null) { + log.error("Invalid script names"); + return false; + } + + envp.put("SERVER_SOFTWARE", "JBossWebServer"); + envp.put("SERVER_NAME", nullsToBlanks(req.getServerName())); + envp.put("GATEWAY_INTERFACE", "CGI/1.1"); + envp.put("SERVER_PROTOCOL", nullsToBlanks(req.getProtocol())); + + int port = req.getServerPort(); + Integer sPort = (port == 0 ? new Integer(-1) : new Integer(port)); + envp.put("SERVER_PORT", sPort.toString()); + + /* + * Local addres and port + */ + envp.put("LOCAL_NAME", nullsToBlanks(req.getLocalName())); + port = req.getLocalPort(); + Integer iPort = (port == 0 ? new Integer(-1) : new Integer(port)); + envp.put("LOCAL_PORT", iPort.toString()); + envp.put("LOCAL_ADDR", nullsToBlanks(req.getLocalAddr())); + + envp.put("REQUEST_METHOD", nullsToBlanks(req.getMethod())); + + /*- + * PATH_INFO should be determined by using sFullName: + * 1) Let sFullName not end in a "/" (see method findScript) + * 2) Let sFullName equal the pathInfo fragment which + * corresponds to the actual script. + * 3) Thus, PATH_INFO = request.getPathInfo().substring( + * sFullName.length()) + * + * (see method findScript, where the real work is done) + * + */ + if (pathInfo == null + || (pathInfo.substring(sFullName.length()).length() <= 0)) { + sPathInfo = ""; + } + else { + sPathInfo = pathInfo.substring(sFullName.length()); + } + envp.put("PATH_INFO", sPathInfo); + + /*- + * PATH_TRANSLATED must be determined after PATH_INFO (and the + * implied real cgi-script) has been taken into account. + * + * The following example demonstrates: + * + * servlet info = /servlet/cgigw/dir1/dir2/cgi1/trans1/trans2 + * cgifullpath = /servlet/cgigw/dir1/dir2/cgi1 + * path_info = /trans1/trans2 + * webAppRootDir = servletContext.getRealPath("/") + * + * path_translated = servletContext.getRealPath("/trans1/trans2") + * + * That is, PATH_TRANSLATED = webAppRootDir + sPathInfo + * (unless sPathInfo is null or blank, then the CGI + * specification dictates that the PATH_TRANSLATED metavariable + * SHOULD NOT be defined. + * + */ + if (sPathInfo != null && !("".equals(sPathInfo))) { + sPathTranslated = context.getRealPath(sPathInfo); + } + else { + sPathTranslated = null; + } + if (sPathTranslated == null || "".equals(sPathTranslated)) { + // Nothing. + } + else { + envp.put("PATH_TRANSLATED", nullsToBlanks(sPathTranslated)); + } + + + envp.put("SCRIPT_NAME", nullsToBlanks(sScriptName)); + envp.put("QUERY_STRING", nullsToBlanks(req.getQueryString())); + envp.put("REMOTE_HOST", nullsToBlanks(req.getRemoteHost())); + envp.put("REMOTE_ADDR", nullsToBlanks(req.getRemoteAddr())); + envp.put("AUTH_TYPE", nullsToBlanks(req.getAuthType())); + envp.put("REMOTE_USER", nullsToBlanks(req.getRemoteUser())); + envp.put("REMOTE_IDENT", ""); //not necessary for full compliance + envp.put("CONTENT_TYPE", nullsToBlanks(req.getContentType())); + + + /* Note CGI spec says CONTENT_LENGTH must be NULL ("") or undefined + * if there is no content, so we cannot put 0 or -1 in as per the + * Servlet API spec. + */ + int contentLength = req.getContentLength(); + String sContentLength = (contentLength <= 0 ? "" : + (new Integer(contentLength)).toString()); + envp.put("CONTENT_LENGTH", sContentLength); + + + Enumeration headers = req.getHeaderNames(); + String header = null; + + while (headers.hasMoreElements()) { + header = null; + header = ((String)headers.nextElement()).toUpperCase(); + //REMIND: rewrite multiple headers as if received as single + //REMIND: change character set + //REMIND: I forgot what the previous REMIND means + if ("AUTHORIZATION".equalsIgnoreCase(header) || + "PROXY_AUTHORIZATION".equalsIgnoreCase(header)) { + //NOOP per CGI specification section 11.2 + } + else { + envp.put("HTTP_" + header.replace('-', '_'), + req.getHeader(header)); + } + } + + scriptFile = new File(sFullPath); + scriptFullPath = scriptFile.getCanonicalPath(); + workingDirectory = new File(scriptFullPath.substring(0, + scriptFullPath.lastIndexOf(File.separator))); + + envp.put("SCRIPT_FILENAME", scriptFullPath); + + envp.put("CONTEXT_PATH", nullsToBlanks(contextPath)); + + String self = ""; + if (contextPath != null && ! "".equals(contextPath) && ! "/".equals(contextPath)) { + self = contextPath; + } + if (servletPath != null && ! "".equals(servletPath) && ! "/".equals(servletPath)) { + self = self.concat(servletPath); + } + + envp.put("PHP_SELF", nullsToBlanks(self)); + + if (req.isSecure()) { + envp.put("HTTPS", "ON"); + envp.put("SSL_CIPHER", req.getAttribute(CIPHER_SUITE)); + envp.put("SSL_SESSION_ID", req.getAttribute(SSL_SESSION)); + envp.put("SSL_CIPHER_USEKEYSIZE", String.valueOf(req.getAttribute(KEY_SIZE))); + X509Certificate[] certs = + (X509Certificate[])req.getAttribute(CERTIFICATE_KEY); + if (certs != null) { + // Well use the first, normaly the client certificate. + envp.put("SSL_SERVER_V_START", certs[0].getNotAfter().toString()); + envp.put("SSL_SERVER_V_END", certs[0].getNotBefore().toString()); + + envp.put("SSL_CLIENT_A_KEY", certs[0].getSigAlgName()); + + // Oops getEncoded gives a DER not PEM encoded ... envp.put("SSL_CLIENT_CERT", certs[0].getEncoded()); + + envp.put("SSL_SERVER_M_SERIAL", certs[0].getSerialNumber().toString()); + envp.put("SSL_SERVER_M_VERSION", String.valueOf(certs[0].getVersion())); + + // Subject + envp.put("SSL_CLIENT_S_DN", certs[0].getSubjectX500Principal().getName()); + // To fill the elements C,ST... Email + String pr = certs[0].getSubjectX500Principal().getName(); + String prs[] = pr.split(", "); + for (int c = 0; c < prs.length; c++) { + String pprs[] = prs[c].split("="); + envp.put("SSL_CLIENT_S_DN_" + pprs[0], pprs[1]); + } + + // Issuer + envp.put("SSL_CLIENT_I_DN", certs[0].getIssuerX500Principal().getName()); + // To fill the elements C,ST... Email Still to TODO. + pr = certs[0].getSubjectX500Principal().getName(); + prs = pr.split(", "); + for (int c = 0; c < prs.length; c++) { + String pprs[] = prs[c].split("="); + envp.put("SSL_CLIENT_I_DN_" + pprs[0], pprs[1]); + } + + + // envp.put("CERT_ISSUER", + // nullsToBlanks(certs[c].getIssuerX500Principal().getName())); + } + } + + this.env = envp; + return true; + } + + + /** + * Creates a CGIEnvironment and derives the necessary environment, + * query parameters, working directory, cgi command, etc. + * + * @param req HttpServletRequest for information provided by + * the Servlet API + * @param context ServletContext for information provided by the + * Servlet API + * + */ + public ScriptEnvironment(HttpServletRequest req, + ServletContext context, + String scriptPathPrefix) + throws IOException + { + this.scriptPathPrefix = scriptPathPrefix; + this.context = context; + this.webAppRootDir = context.getRealPath("/"); + this.tempDir = (File)context.getAttribute(Globals.WORK_DIR_ATTR); + + + if (req.getAttribute(Globals.INCLUDE_CONTEXT_PATH_ATTR) != null) { + // Include + this.contextPath = (String) req.getAttribute(Globals.INCLUDE_CONTEXT_PATH_ATTR); + this.servletPath = (String) req.getAttribute(Globals.INCLUDE_SERVLET_PATH_ATTR); + this.pathInfo = (String) req.getAttribute(Globals.INCLUDE_PATH_INFO_ATTR); + } + else { + // Direct call + this.contextPath = req.getContextPath(); + this.servletPath = req.getServletPath(); + this.pathInfo = req.getPathInfo(); + } + + // If getPathInfo() returns null, must be using extension mapping + // In this case, pathInfo should be same as servletPath + if (this.pathInfo == null) { + this.pathInfo = this.servletPath; + } + this.valid = setEnvironment(req); + } + + + /** + * Gets derived script full path + * + * @return full script path + * + */ + public String getFullPath() + { + return scriptFullPath; + } + + /** + * Gets derived Script file + * + * @return Script file + * + */ + public File getScriptFile() + { + return scriptFile; + } + + /** + * Gets derived Script working directory + * + * @return working directory + * + */ + public File getWorkingDirectory() + { + return workingDirectory; + } + + /** + * Gets derived Script environment + * + * @return Script environment + * + */ + public Hashtable getEnvironment() + { + return env; + } + + /** + * Gets derived Script query parameters + * + * @return Script query parameters + * + */ + public ArrayList getParameters() + { + return queryParameters; + } + + /** + * Gets validity status + * + * @return true if this environment is valid, false + * otherwise + * + */ + public boolean isValid() + { + return valid; + } + + /** + * Converts null strings to blank strings ("") + * + * @param s string to be converted if necessary + * @return a non-null string, either the original or the empty string + * ("") if the original was null + */ + protected String nullsToBlanks(String s) + { + return nullsToString(s, ""); + } + + /** + * Converts null strings to another string + * + * @param couldBeNull string to be converted if necessary + * @param subForNulls string to return instead of a null string + * @return a non-null string, either the original or the substitute + * string if the original was null + */ + protected String nullsToString(String couldBeNull, + String subForNulls) + { + return (couldBeNull == null ? subForNulls : couldBeNull); + } + + /** + * Converts blank strings to another string + * + * @param couldBeBlank string to be converted if necessary + * @param subForBlanks string to return instead of a blank string + * @return a non-null string, either the original or the substitute + * string if the original was null or empty ("") + */ + protected String blanksToString(String couldBeBlank, + String subForBlanks) + { + return (("".equals(couldBeBlank) || couldBeBlank == null) + ? subForBlanks + : couldBeBlank); + } + + /** + * Converts Environment Hastable to String array + * + * @return Srring array containing name value pairs. + * @exception NullPointerException if a hash key has a null value + */ + public String[] getEnvironmentArray() + throws NullPointerException + { + return hashToStringArray(env); + } + + /** + * Converts a Hashtable to a String array by converting each + * key/value pair in the Hashtable to two consecutive Strings + * + * @param h Hashtable to convert + * @return converted string array + * @exception NullPointerException if a hash key has a null value + */ + public static String[] hashToStringArray(Hashtable h) + throws NullPointerException + { + Vector v = new Vector(); + Enumeration e = h.keys(); + while (e.hasMoreElements()) { + String k = e.nextElement().toString(); + v.add(k); + v.add(h.get(k)); + } + String[] strArr = new String[v.size()]; + v.copyInto(strArr); + return strArr; + } + +} Index: 3rdParty_sources/jbossweb/org/jboss/web/rewrite/Resolver.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/web/rewrite/Resolver.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/web/rewrite/Resolver.java 17 Aug 2012 14:43:39 -0000 1.1 @@ -0,0 +1,40 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2006, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + + +package org.jboss.web.rewrite; + +public abstract class Resolver { + + public abstract String resolve(String key); + + public String resolveEnv(String key) { + return System.getProperty(key); + } + + public abstract String resolveSsl(String key); + + public abstract String resolveHttp(String key); + + public abstract boolean resolveResource(int type, String name); + +} \ No newline at end of file Index: 3rdParty_sources/jbossweb/org/jboss/web/rewrite/RewriteCond.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/web/rewrite/RewriteCond.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/web/rewrite/RewriteCond.java 17 Aug 2012 14:43:39 -0000 1.1 @@ -0,0 +1,273 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2006, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + + +package org.jboss.web.rewrite; + +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class RewriteCond { + + public abstract class Condition { + public abstract boolean evaluate(String value, Resolver resolver); + } + + public class PatternCondition extends Condition { + public Pattern pattern; + public Matcher matcher = null; + public boolean evaluate(String value, Resolver resolver) { + Matcher m = pattern.matcher(value); + if (m.matches()) { + matcher = m; + return true; + } else { + return false; + } + } + } + + public class LexicalCondition extends Condition { + /** + * -1: < + * 0: = + * 1: > + */ + public int type = 0; + public String condition; + public boolean evaluate(String value, Resolver resolver) { + int result = value.compareTo(condition); + switch (type) { + case -1: + return (result < 0); + case 0: + return (result == 0); + case 1: + return (result > 0); + default: + return false; + } + + } + } + + public class ResourceCondition extends Condition { + /** + * 0: -d (is directory ?) + * 1: -f (is regular file ?) + * 2: -s (is regular file with size ?) + */ + public int type = 0; + public boolean evaluate(String value, Resolver resolver) { + switch (type) { + case 0: + return true; + case 1: + return true; + case 2: + return true; + default: + return false; + } + + } + } + + protected String testString = null; + protected String condPattern = null; + + public String getCondPattern() { + return condPattern; + } + + public void setCondPattern(String condPattern) { + this.condPattern = condPattern; + } + + public String getTestString() { + return testString; + } + + public void setTestString(String testString) { + this.testString = testString; + } + + public void parse(Map maps) { + test = new Substitution(); + test.setSub(testString); + test.parse(maps); + if (condPattern.startsWith("!")) { + positive = false; + condPattern = condPattern.substring(1); + } + if (condPattern.startsWith("<")) { + LexicalCondition condition = new LexicalCondition(); + condition.type = -1; + condition.condition = condPattern.substring(1); + } else if (condPattern.startsWith(">")) { + LexicalCondition condition = new LexicalCondition(); + condition.type = 1; + condition.condition = condPattern.substring(1); + } else if (condPattern.startsWith("=")) { + LexicalCondition condition = new LexicalCondition(); + condition.type = 0; + condition.condition = condPattern.substring(1); + } else if (condPattern.equals("-d")) { + ResourceCondition ncondition = new ResourceCondition(); + ncondition.type = 0; + } else if (condPattern.equals("-f")) { + ResourceCondition ncondition = new ResourceCondition(); + ncondition.type = 1; + } else if (condPattern.equals("-s")) { + ResourceCondition ncondition = new ResourceCondition(); + ncondition.type = 2; + } else { + PatternCondition condition = new PatternCondition(); + int flags = 0; + if (isNocase()) { + flags |= Pattern.CASE_INSENSITIVE; + } + condition.pattern = Pattern.compile(condPattern, flags); + } + } + + public Matcher getMatcher() { + Object condition = this.condition.get(); + if (condition instanceof PatternCondition) { + return ((PatternCondition) condition).matcher; + } + return null; + } + + /** + * String representation. + */ + public String toString() { + // FIXME: Add flags if possible + return "RewriteCond " + testString + " " + condPattern; + } + + + protected boolean positive = true; + + protected Substitution test = null; + + protected ThreadLocal condition = new ThreadLocal(); + + /** + * This makes the test case-insensitive, i.e., there is no difference between + * 'A-Z' and 'a-z' both in the expanded TestString and the CondPattern. This + * flag is effective only for comparisons between TestString and CondPattern. + * It has no effect on filesystem and subrequest checks. + */ + public boolean nocase = false; + + /** + * Use this to combine rule conditions with a local OR instead of the implicit AND. + */ + public boolean ornext = false; + + /** + * Evaluate the condition based on the context + * + * @param rule corresponding matched rule + * @param cond last matched condition + * @return + */ + public boolean evaluate(Matcher rule, Matcher cond, Resolver resolver) { + String value = test.evaluate(rule, cond, resolver); + if (nocase) { + value = value.toLowerCase(); + } + Condition condition = this.condition.get(); + if (condition == null) { + if (condPattern.startsWith("<")) { + LexicalCondition ncondition = new LexicalCondition(); + ncondition.type = -1; + ncondition.condition = condPattern.substring(1); + condition = ncondition; + } else if (condPattern.startsWith(">")) { + LexicalCondition ncondition = new LexicalCondition(); + ncondition.type = 1; + ncondition.condition = condPattern.substring(1); + condition = ncondition; + } else if (condPattern.startsWith("=")) { + LexicalCondition ncondition = new LexicalCondition(); + ncondition.type = 0; + ncondition.condition = condPattern.substring(1); + condition = ncondition; + } else if (condPattern.equals("-d")) { + ResourceCondition ncondition = new ResourceCondition(); + ncondition.type = 0; + condition = ncondition; + } else if (condPattern.equals("-f")) { + ResourceCondition ncondition = new ResourceCondition(); + ncondition.type = 1; + condition = ncondition; + } else if (condPattern.equals("-s")) { + ResourceCondition ncondition = new ResourceCondition(); + ncondition.type = 2; + condition = ncondition; + } else { + PatternCondition ncondition = new PatternCondition(); + int flags = 0; + if (isNocase()) { + flags |= Pattern.CASE_INSENSITIVE; + } + ncondition.pattern = Pattern.compile(condPattern, flags); + condition = ncondition; + } + this.condition.set(condition); + } + if (positive) { + return condition.evaluate(value, resolver); + } else { + return !condition.evaluate(value, resolver); + } + } + + public boolean isNocase() { + return nocase; + } + + public void setNocase(boolean nocase) { + this.nocase = nocase; + } + + public boolean isOrnext() { + return ornext; + } + + public void setOrnext(boolean ornext) { + this.ornext = ornext; + } + + public boolean isPositive() { + return positive; + } + + public void setPositive(boolean positive) { + this.positive = positive; + } + +} Index: 3rdParty_sources/jbossweb/org/jboss/web/rewrite/RewriteMap.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/web/rewrite/RewriteMap.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/web/rewrite/RewriteMap.java 17 Aug 2012 14:43:39 -0000 1.1 @@ -0,0 +1,32 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2006, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + + +package org.jboss.web.rewrite; + +public interface RewriteMap { + + public String setParameters(String params); + + public String lookup(String key); + +} Index: 3rdParty_sources/jbossweb/org/jboss/web/rewrite/RewriteRule.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/web/rewrite/RewriteRule.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/web/rewrite/RewriteRule.java 17 Aug 2012 14:43:39 -0000 1.1 @@ -0,0 +1,539 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2006, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + + +package org.jboss.web.rewrite; + +import java.util.ArrayList; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class RewriteRule { + + protected RewriteCond[] conditions = new RewriteCond[0]; + + protected ThreadLocal pattern = new ThreadLocal(); + protected Substitution substitution = null; + + protected String patternString = null; + protected String substitutionString = null; + + public void parse(Map maps) { + // Parse the substitution + if (!"-".equals(substitutionString)) { + substitution = new Substitution(); + substitution.setSub(substitutionString); + substitution.parse(maps); + } + // Parse the pattern + int flags = 0; + if (isNocase()) { + flags |= Pattern.CASE_INSENSITIVE; + } + Pattern.compile(patternString, flags); + // Parse conditions + for (int i = 0; i < conditions.length; i++) { + conditions[i].parse(maps); + } + // Parse flag which have substitution values + if (isEnv()) { + for (int i = 0; i < envValue.size(); i++) { + Substitution newEnvSubstitution = new Substitution(); + newEnvSubstitution.setSub(envValue.get(i)); + newEnvSubstitution.parse(maps); + envSubstitution.add(newEnvSubstitution); + envResult.add(new ThreadLocal()); + } + } + if (isCookie()) { + cookieSubstitution = new Substitution(); + cookieSubstitution.setSub(cookieValue); + cookieSubstitution.parse(maps); + } + } + + public void addCondition(RewriteCond condition) { + RewriteCond[] conditions = new RewriteCond[this.conditions.length + 1]; + for (int i = 0; i < this.conditions.length; i++) { + conditions[i] = this.conditions[i]; + } + conditions[this.conditions.length] = condition; + this.conditions = conditions; + } + + /** + * Evaluate the rule based on the context + * + * @return null if no rewrite took place + */ + public CharSequence evaluate(CharSequence url, Resolver resolver) { + Pattern pattern = this.pattern.get(); + if (pattern == null) { + // Parse the pattern + int flags = 0; + if (isNocase()) { + flags |= Pattern.CASE_INSENSITIVE; + } + pattern = Pattern.compile(patternString, flags); + this.pattern.set(pattern); + } + Matcher matcher = pattern.matcher(url); + if (!matcher.matches()) { + // Evaluation done + return null; + } + // Evaluate conditions + boolean done = false; + boolean rewrite = true; + Matcher lastMatcher = null; + int pos = 0; + while (!done) { + if (pos < conditions.length) { + rewrite = conditions[pos].evaluate(matcher, lastMatcher, resolver); + if (rewrite) { + Matcher lastMatcher2 = conditions[pos].getMatcher(); + if (lastMatcher2 != null) { + lastMatcher = lastMatcher2; + } + while (pos < conditions.length && conditions[pos].isOrnext()) { + pos++; + } + } else if (!conditions[pos].isOrnext()) { + done = true; + } + pos++; + } else { + done = true; + } + } + // Use the substitution to rewrite the url + if (rewrite) { + if (isEnv()) { + for (int i = 0; i < envSubstitution.size(); i++) { + envResult.get(i).set(envSubstitution.get(i).evaluate(matcher, lastMatcher, resolver)); + } + } + if (isCookie()) { + cookieResult.set(cookieSubstitution.evaluate(matcher, lastMatcher, resolver)); + } + if (substitution != null) { + return substitution.evaluate(matcher, lastMatcher, resolver); + } else { + return url; + } + } else { + return null; + } + } + + + /** + * String representation. + */ + public String toString() { + // FIXME: Add flags if possible + return "RewriteRule " + patternString + " " + substitutionString; + } + + + /** + * This flag chains the current rule with the next rule (which itself + * can be chained with the following rule, etc.). This has the following + * effect: if a rule matches, then processing continues as usual, i.e., + * the flag has no effect. If the rule does not match, then all following + * chained rules are skipped. For instance, use it to remove the ``.www'' + * part inside a per-directory rule set when you let an external redirect + * happen (where the ``.www'' part should not to occur!). + */ + protected boolean chain = false; + + /** + * This sets a cookie on the client's browser. The cookie's name is + * specified by NAME and the value is VAL. The domain field is the domain + * of the cookie, such as '.apache.org',the optional lifetime + * is the lifetime of the cookie in minutes, and the optional path is the + * path of the cookie + */ + protected boolean cookie = false; + protected String cookieName = null; + protected String cookieValue = null; + protected String cookieDomain = null; + protected int cookieLifetime = -1; + protected String cookiePath = null; + protected boolean cookieSecure = false; + protected boolean cookieHttpOnly = false; + protected Substitution cookieSubstitution = null; + protected ThreadLocal cookieResult = new ThreadLocal(); + + /** + * This forces a request attribute named VAR to be set to the value VAL, + * where VAL can contain regexp back references $N and %N which will be + * expanded. Multiple env flags are allowed. + */ + protected boolean env = false; + protected ArrayList envName = new ArrayList(); + protected ArrayList envValue = new ArrayList(); + protected ArrayList envSubstitution = new ArrayList(); + protected ArrayList> envResult = new ArrayList>(); + + /** + * This forces the current URL to be forbidden, i.e., it immediately sends + * back a HTTP response of 403 (FORBIDDEN). Use this flag in conjunction + * with appropriate RewriteConds to conditionally block some URLs. + */ + protected boolean forbidden = false; + + /** + * This forces the current URL to be gone, i.e., it immediately sends + * back a HTTP response of 410 (GONE). Use this flag to mark pages which + * no longer exist as gone. + */ + protected boolean gone = false; + + /** + * Host. This means this rule and its associated conditions will apply to + * host, allowing host rewriting (ex: redirecting internally *.foo.com to + * bar.foo.com). + */ + protected boolean host = false; + + /** + * Stop the rewriting process here and don't apply any more rewriting + * rules. This corresponds to the Perl last command or the break command + * from the C language. Use this flag to prevent the currently rewritten + * URL from being rewritten further by following rules. For example, use + * it to rewrite the root-path URL ('/') to a real one, e.g., '/e/www/'. + */ + protected boolean last = false; + + /** + * Re-run the rewriting process (starting again with the first rewriting + * rule). Here the URL to match is again not the original URL but the URL + * from the last rewriting rule. This corresponds to the Perl next + * command or the continue command from the C language. Use this flag to + * restart the rewriting process, i.e., to immediately go to the top of + * the loop. But be careful not to create an infinite loop! + */ + protected boolean next = false; + + /** + * This makes the Pattern case-insensitive, i.e., there is no difference + * between 'A-Z' and 'a-z' when Pattern is matched against the current + * URL. + */ + protected boolean nocase = false; + + /** + * This flag keeps mod_rewrite from applying the usual URI escaping rules + * to the result of a rewrite. Ordinarily, special characters (such as + * '%', '$', ';', and so on) will be escaped into their hexcode + * equivalents ('%25', '%24', and '%3B', respectively); this flag + * prevents this from being done. This allows percent symbols to appear + * in the output, as in + * RewriteRule /foo/(.*) /bar?arg=P1\%3d$1 [R,NE] + * which would turn '/foo/zed' into a safe request for '/bar?arg=P1=zed'. + */ + protected boolean noescape = false; + + /** + * This flag forces the rewriting engine to skip a rewriting rule if the + * current request is an internal sub-request. For instance, sub-requests + * occur internally in Apache when mod_include tries to find out + * information about possible directory default files (index.xxx). On + * sub-requests it is not always useful and even sometimes causes a + * failure to if the complete set of rules are applied. Use this flag to + * exclude some rules. Use the following rule for your decision: whenever + * you prefix some URLs with CGI-scripts to force them to be processed by + * the CGI-script, the chance is high that you will run into problems (or + * even overhead) on sub-requests. In these cases, use this flag. + */ + protected boolean nosubreq = false; + + /** + * This flag forces the substitution part to be internally forced as a proxy + * request and immediately (i.e., rewriting rule processing stops here) put + * through the proxy module. You have to make sure that the substitution string + * is a valid URI (e.g., typically starting with http://hostname) which can be + * handled by the Apache proxy module. If not you get an error from the proxy + * module. Use this flag to achieve a more powerful implementation of the + * ProxyPass directive, to map some remote stuff into the namespace of + * the local server. + * FIXME: No proxy + */ + + /** + * FIXME: No passthrough ? + */ + + /** + * This flag forces the rewriting engine to append a query string part in + * the substitution string to the existing one instead of replacing it. + * Use this when you want to add more data to the query string via + * a rewrite rule. + */ + protected boolean qsappend = false; + + /** + * Prefix Substitution with http://thishost[:thisport]/ (which makes the + * new URL a URI) to force a external redirection. If no code is given + * a HTTP response of 302 (MOVED TEMPORARILY) is used. If you want to + * use other response codes in the range 300-400 just specify them as + * a number or use one of the following symbolic names: temp (default), + * permanent, seeother. Use it for rules which should canonicalize the + * URL and give it back to the client, e.g., translate ``/~'' into ``/u/'' + * or always append a slash to /u/user, etc. Note: When you use this flag, + * make sure that the substitution field is a valid URL! If not, you are + * redirecting to an invalid location! And remember that this flag itself + * only prefixes the URL with http://thishost[:thisport]/, rewriting + * continues. Usually you also want to stop and do the redirection + * immediately. To stop the rewriting you also have to provide the + * 'L' flag. + */ + protected boolean redirect = false; + protected int redirectCode = 0; + + /** + * This flag forces the rewriting engine to skip the next num rules in + * sequence when the current rule matches. Use this to make pseudo + * if-then-else constructs: The last rule of the then-clause becomes + * skip=N where N is the number of rules in the else-clause. + * (This is not the same as the 'chain|C' flag!) + */ + protected int skip = 0; + + /** + * Force the MIME-type of the target file to be MIME-type. For instance, + * this can be used to setup the content-type based on some conditions. + * For example, the following snippet allows .php files to be displayed + * by mod_php if they are called with the .phps extension: + * RewriteRule ^(.+\.php)s$ $1 [T=application/x-httpd-php-source] + */ + protected boolean type = false; + protected String typeValue = null; + public boolean isChain() { + return chain; + } + public void setChain(boolean chain) { + this.chain = chain; + } + public RewriteCond[] getConditions() { + return conditions; + } + public void setConditions(RewriteCond[] conditions) { + this.conditions = conditions; + } + public boolean isCookie() { + return cookie; + } + public void setCookie(boolean cookie) { + this.cookie = cookie; + } + public String getCookieName() { + return cookieName; + } + public void setCookieName(String cookieName) { + this.cookieName = cookieName; + } + public String getCookieValue() { + return cookieValue; + } + public void setCookieValue(String cookieValue) { + this.cookieValue = cookieValue; + } + public String getCookieResult() { + return cookieResult.get(); + } + public boolean isEnv() { + return env; + } + public int getEnvSize() { + return envName.size(); + } + public void setEnv(boolean env) { + this.env = env; + } + public String getEnvName(int i) { + return envName.get(i); + } + public void addEnvName(String envName) { + this.envName.add(envName); + } + public String getEnvValue(int i) { + return envValue.get(i); + } + public void addEnvValue(String envValue) { + this.envValue.add(envValue); + } + public String getEnvResult(int i) { + return envResult.get(i).get(); + } + public boolean isForbidden() { + return forbidden; + } + public void setForbidden(boolean forbidden) { + this.forbidden = forbidden; + } + public boolean isGone() { + return gone; + } + public void setGone(boolean gone) { + this.gone = gone; + } + public boolean isLast() { + return last; + } + public void setLast(boolean last) { + this.last = last; + } + public boolean isNext() { + return next; + } + public void setNext(boolean next) { + this.next = next; + } + public boolean isNocase() { + return nocase; + } + public void setNocase(boolean nocase) { + this.nocase = nocase; + } + public boolean isNoescape() { + return noescape; + } + public void setNoescape(boolean noescape) { + this.noescape = noescape; + } + public boolean isNosubreq() { + return nosubreq; + } + public void setNosubreq(boolean nosubreq) { + this.nosubreq = nosubreq; + } + public boolean isQsappend() { + return qsappend; + } + public void setQsappend(boolean qsappend) { + this.qsappend = qsappend; + } + public boolean isRedirect() { + return redirect; + } + public void setRedirect(boolean redirect) { + this.redirect = redirect; + } + public int getRedirectCode() { + return redirectCode; + } + public void setRedirectCode(int redirectCode) { + this.redirectCode = redirectCode; + } + public int getSkip() { + return skip; + } + public void setSkip(int skip) { + this.skip = skip; + } + public Substitution getSubstitution() { + return substitution; + } + public void setSubstitution(Substitution substitution) { + this.substitution = substitution; + } + public boolean isType() { + return type; + } + public void setType(boolean type) { + this.type = type; + } + public String getTypeValue() { + return typeValue; + } + public void setTypeValue(String typeValue) { + this.typeValue = typeValue; + } + + public String getPatternString() { + return patternString; + } + + public void setPatternString(String patternString) { + this.patternString = patternString; + } + + public String getSubstitutionString() { + return substitutionString; + } + + public void setSubstitutionString(String substitutionString) { + this.substitutionString = substitutionString; + } + + public boolean isHost() { + return host; + } + + public void setHost(boolean host) { + this.host = host; + } + + public String getCookieDomain() { + return cookieDomain; + } + + public void setCookieDomain(String cookieDomain) { + this.cookieDomain = cookieDomain; + } + + public int getCookieLifetime() { + return cookieLifetime; + } + + public void setCookieLifetime(int cookieLifetime) { + this.cookieLifetime = cookieLifetime; + } + + public String getCookiePath() { + return cookiePath; + } + + public void setCookiePath(String cookiePath) { + this.cookiePath = cookiePath; + } + + public boolean isCookieSecure() { + return cookieSecure; + } + + public void setCookieSecure(boolean cookieSecure) { + this.cookieSecure = cookieSecure; + } + + public boolean isCookieHttpOnly() { + return cookieHttpOnly; + } + + public void setCookieHttpOnly(boolean cookieHttpOnly) { + this.cookieHttpOnly = cookieHttpOnly; + } + +} Index: 3rdParty_sources/jbossweb/org/jboss/web/rewrite/RewriteValve.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/web/rewrite/RewriteValve.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/web/rewrite/RewriteValve.java 17 Aug 2012 14:43:39 -0000 1.1 @@ -0,0 +1,733 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2006, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + + +package org.jboss.web.rewrite; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.servlet.ServletException; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletResponse; + +import org.apache.catalina.Container; +import org.apache.catalina.Context; +import org.apache.catalina.Engine; +import org.apache.catalina.Host; +import org.apache.catalina.Lifecycle; +import org.apache.catalina.LifecycleException; +import org.apache.catalina.LifecycleListener; +import org.apache.catalina.connector.Request; +import org.apache.catalina.connector.Response; +import org.apache.catalina.util.LifecycleSupport; +import org.apache.catalina.valves.ValveBase; +import org.apache.tomcat.util.buf.CharChunk; +import org.apache.tomcat.util.buf.MessageBytes; +import org.apache.tomcat.util.http.TomcatCookie; +import org.apache.tomcat.util.net.URL; + +public class RewriteValve extends ValveBase + implements Lifecycle { + + /** + * The lifecycle event support for this component. + */ + protected LifecycleSupport lifecycle = new LifecycleSupport(this); + + + /** + * The rewrite rules that the valve will use. + */ + protected RewriteRule[] rules = null; + + + /** + * If rewriting occurs, the whole request will be processed again. + */ + protected ThreadLocal invoked = new ThreadLocal(); + + + /** + * Relative path to the configuration file. + * Note: If the valve's container is a context, this will be relative to + * /WEB-INF/. + */ + protected String resourcePath = "rewrite.properties"; + + + /** + * Will be set to true if the valve is associated with a context. + */ + protected boolean context = false; + + + /** + * Maps to be used by the rules. + */ + protected Map maps = new Hashtable(); + + + public void addLifecycleListener(LifecycleListener listener) { + lifecycle.addLifecycleListener(listener); + } + + public LifecycleListener[] findLifecycleListeners() { + return lifecycle.findLifecycleListeners(); + } + + public void removeLifecycleListener(LifecycleListener listener) { + lifecycle.removeLifecycleListener(listener); + } + + public void start() throws LifecycleException { + + InputStream is = null; + + // Process configuration file for this valve + if (getContainer() instanceof Context) { + context = true; + is = ((Context) getContainer()).getServletContext() + .getResourceAsStream("/WEB-INF/" + resourcePath); + if ((is == null) && (container.getLogger().isInfoEnabled())) { + container.getLogger().info("No configuration resource found: /WEB-INF/" + resourcePath); + } + } else { + String resourceName = getHostConfigPath(resourcePath); + File file = new File(getConfigBase(), resourceName); + try { + if (!file.exists()) { + if (resourceName != null) { + // Use getResource and getResourceAsStream + is = getClass().getClassLoader() + .getResourceAsStream(resourceName); + if (is != null && container.getLogger().isDebugEnabled()) { + container.getLogger().debug("Read configuration from CL at " + resourceName); + } + } + } else { + if (container.getLogger().isDebugEnabled()) { + container.getLogger().debug("Read configuration from " + file.getAbsolutePath()); + } + is = new FileInputStream(file); + } + if ((is == null) && (container.getLogger().isInfoEnabled())) { + container.getLogger().info("No configuration resource found: " + resourceName + + " in " + getConfigBase().getAbsolutePath() + " or in the classloader"); + } + } catch (Exception e) { + container.getLogger().error("Error opening configuration", e); + } + } + + if (is == null) { + // Will use management operations to configure the valve dynamically + return; + } + + BufferedReader reader = new BufferedReader(new InputStreamReader(is)); + + try { + parse(reader); + } finally { + try { + reader.close(); + } catch (IOException e) { + container.getLogger().error("Error closing configuration", e); + } + try { + if (is != null) { + is.close(); + } + } catch (IOException e) { + container.getLogger().error("Error closing configuration", e); + } + } + + } + + public void setConfiguration(String configuration) + throws Exception { + maps.clear(); + parse(new BufferedReader(new StringReader(configuration))); + } + + public String getConfiguration() { + StringBuffer buffer = new StringBuffer(); + // FIXME: Output maps if possible + for (int i = 0; i < rules.length; i++) { + for (int j = 0; j < rules[i].getConditions().length; j++) { + buffer.append(rules[i].getConditions()[j].toString()).append("\r\n"); + } + buffer.append(rules[i].toString()).append("\r\n").append("\r\n"); + } + return buffer.toString(); + } + + protected void parse(BufferedReader reader) throws LifecycleException { + ArrayList rules = new ArrayList(); + ArrayList conditions = new ArrayList(); + while (true) { + try { + String line = reader.readLine(); + if (line == null) { + break; + } + Object result = parse(line); + if (result instanceof RewriteRule) { + RewriteRule rule = (RewriteRule) result; + if (container.getLogger().isDebugEnabled()) { + container.getLogger().debug("Add rule with pattern " + rule.getPatternString() + + " and substitution " + rule.getSubstitutionString()); + } + for (int i = (conditions.size() - 1); i > 0; i--) { + if (conditions.get(i - 1).isOrnext()) { + conditions.get(i).setOrnext(true); + } + } + for (int i = 0; i < conditions.size(); i++) { + if (container.getLogger().isDebugEnabled()) { + RewriteCond cond = conditions.get(i); + container.getLogger().debug("Add condition " + cond.getCondPattern() + + " test " + cond.getTestString() + " to rule with pattern " + + rule.getPatternString() + " and substitution " + + rule.getSubstitutionString() + (cond.isOrnext() ? " [OR]" : "") + + (cond.isNocase() ? " [NC]" : "")); + } + rule.addCondition(conditions.get(i)); + } + conditions.clear(); + rules.add(rule); + } else if (result instanceof RewriteCond) { + conditions.add((RewriteCond) result); + } else if (result instanceof Object[]) { + String mapName = (String) ((Object[]) result)[0]; + RewriteMap map = (RewriteMap) ((Object[]) result)[1]; + maps.put(mapName, map); + if (map instanceof Lifecycle) { + ((Lifecycle) map).start(); + } + } + } catch (IOException e) { + container.getLogger().error("Error reading configuration", e); + } + } + this.rules = (RewriteRule[]) rules.toArray(new RewriteRule[0]); + + // Finish parsing the rules + for (int i = 0; i < this.rules.length; i++) { + this.rules[i].parse(maps); + } + } + + public void stop() throws LifecycleException { + Iterator values = maps.values().iterator(); + while (values.hasNext()) { + RewriteMap map = values.next(); + if (map instanceof Lifecycle) { + ((Lifecycle) map).stop(); + } + } + maps.clear(); + rules = null; + } + + + public void invoke(Request request, Response response) + throws IOException, ServletException { + + if (rules == null || rules.length == 0) { + getNext().invoke(request, response); + return; + } + + if (invoked.get() == Boolean.TRUE) { + getNext().invoke(request, response); + invoked.set(null); + return; + } + + TomcatResolver resolver = new TomcatResolver(request); + + invoked.set(Boolean.TRUE); + + // As long as MB isn't a char sequence or affiliated, this has to be + // converted to a string + MessageBytes urlMB = context ? request.getRequestPathMB() : request.getDecodedRequestURIMB(); + urlMB.toChars(); + CharSequence url = urlMB.getCharChunk(); + CharSequence host = request.getServerName(); + boolean rewritten = false; + boolean done = false; + for (int i = 0; i < rules.length; i++) { + CharSequence test = (rules[i].isHost()) ? host : url; + CharSequence newtest = rules[i].evaluate(test, resolver); + if (newtest != null && !test.equals(newtest.toString())) { + if (container.getLogger().isDebugEnabled()) { + container.getLogger().debug("Rewrote " + test + " as " + newtest + + " with rule pattern " + rules[i].getPatternString()); + } + if (rules[i].isHost()) { + host = newtest; + } else { + url = newtest; + } + rewritten = true; + } + + // Final reply + + // - forbidden + if (rules[i].isForbidden() && newtest != null) { + response.sendError(HttpServletResponse.SC_FORBIDDEN); + done = true; + break; + } + // - gone + if (rules[i].isGone() && newtest != null) { + response.sendError(HttpServletResponse.SC_GONE); + done = true; + break; + } + // - redirect (code) + if (rules[i].isRedirect() && newtest != null) { + // append the query string to the url if there is one and it hasn't been rewritten + String queryString = request.getQueryString(); + StringBuffer urlString = new StringBuffer(url); + if (queryString != null && queryString.length() > 0) { + int index = urlString.indexOf("?"); + if (index != -1) { + // if qsa is specified append the query + if (rules[i].isQsappend()) { + urlString.append('&'); + urlString.append(queryString); + } + // if the ? is the last character delete it, its only purpose was to + // prevent the rewrite module from appending the query string + else if (index == urlString.length() - 1) { + urlString.deleteCharAt(index); + } + } else { + urlString.append('?'); + urlString.append(queryString); + } + } + // Insert the context if + // 1. this valve is associated with a context + // 2. the url starts with a leading slash + // 3. the url isn't absolute + if (context && urlString.charAt(0) == '/' && !hasScheme(urlString)) { + urlString.insert(0, request.getContext().getEncodedPath()); + } + response.sendRedirect(urlString.toString()); + response.setStatus(rules[i].getRedirectCode()); + done = true; + break; + } + + // Reply modification + + // - cookie + if (rules[i].isCookie() && newtest != null) { + TomcatCookie cookie = new TomcatCookie(rules[i].getCookieName(), + rules[i].getCookieResult()); + cookie.setDomain(rules[i].getCookieDomain()); + cookie.setMaxAge(rules[i].getCookieLifetime()); + cookie.setPath(rules[i].getCookiePath()); + cookie.setSecure(rules[i].isCookieSecure()); + cookie.setHttpOnly(rules[i].isCookieHttpOnly()); + response.addCookie(cookie); + } + // - env (note: this sets a request attribute) + if (rules[i].isEnv() && newtest != null) { + for (int j = 0; j < rules[i].getEnvSize(); j++) { + request.setAttribute(rules[i].getEnvName(j), rules[i].getEnvResult(j)); + } + } + // - content type (note: this will not force the content type, use a filter + // to do that) + if (rules[i].isType() && newtest != null) { + request.setContentType(rules[i].getTypeValue()); + } + // - qsappend + if (rules[i].isQsappend() && newtest != null) { + String queryString = request.getQueryString(); + String urlString = url.toString(); + if (urlString.indexOf('?') != -1 && queryString != null) { + url = urlString + "&" + queryString; + } + } + + // Control flow processing + + // - chain (skip remaining chained rules if this one does not match) + if (rules[i].isChain() && newtest == null) { + for (int j = i; j < rules.length; j++) { + if (!rules[j].isChain()) { + i = j; + break; + } + } + continue; + } + // - last (stop rewriting here) + if (rules[i].isLast() && newtest != null) { + break; + } + // - next (redo again) + if (rules[i].isNext() && newtest != null) { + i = 0; + continue; + } + // - skip (n rules) + if (newtest != null) { + i += rules[i].getSkip(); + } + + } + + if (rewritten) { + if (!done) { + // See if we need to replace the query string + String urlString = url.toString(); + String queryString = null; + int queryIndex = urlString.indexOf('?'); + if (queryIndex != -1) { + queryString = urlString.substring(queryIndex+1); + urlString = urlString.substring(0, queryIndex); + } + // Set the new URL + CharChunk chunk = request.getCoyoteRequest().requestURI().getCharChunk(); + chunk.recycle(); + if (context) { + chunk.append(request.getContextPath()); + } + chunk.append(urlString); + request.getCoyoteRequest().requestURI().toChars(); + // Set the new Query if there is one + if (queryString != null) { + chunk = request.getCoyoteRequest().queryString().getCharChunk(); + chunk.recycle(); + chunk.append(queryString); + request.getCoyoteRequest().queryString().toChars(); + } + // Set the new host if it changed + if (!host.equals(request.getServerName())) { + chunk = request.getCoyoteRequest().serverName().getCharChunk(); + chunk.recycle(); + chunk.append(host.toString()); + request.getCoyoteRequest().serverName().toChars(); + } + request.getMappingData().recycle(); + // Reinvoke the whole request recursively + try { + request.getConnector().getProtocolHandler().getAdapter().service + (request.getCoyoteRequest(), response.getCoyoteResponse()); + } catch (Exception e) { + // This doesn't actually happen in the Catalina adapter implementation + } + } + } else { + getNext().invoke(request, response); + } + + invoked.set(null); + + } + + + /** + * Get config base. + */ + protected File getConfigBase() { + File configBase = + new File(System.getProperty("catalina.base"), "conf"); + if (!configBase.exists()) { + return null; + } else { + return configBase; + } + } + + + /** + * Find the configuration path where the rewrite configuration file + * will be stored. + * + * @param resourceName + * @return + */ + protected String getHostConfigPath(String resourceName) { + StringBuffer result = new StringBuffer(); + Container container = getContainer(); + Container host = null; + Container engine = null; + while (container != null) { + if (container instanceof Host) + host = container; + if (container instanceof Engine) + engine = container; + container = container.getParent(); + } + if (engine != null) { + result.append(engine.getName()).append('/'); + } + if (host != null) { + result.append(host.getName()).append('/'); + } + result.append(resourceName); + return result.toString(); + } + + + /** + * This factory method will parse a line formed like: + * + * Example: + * RewriteCond %{REMOTE_HOST} ^host1.* [OR] + * + * @param line + * @return + */ + public static Object parse(String line) { + StringTokenizer tokenizer = new StringTokenizer(line); + if (tokenizer.hasMoreTokens()) { + String token = tokenizer.nextToken(); + if (token.equals("RewriteCond")) { + // RewriteCond TestString CondPattern [Flags] + RewriteCond condition = new RewriteCond(); + if (tokenizer.countTokens() < 2) { + throw new IllegalArgumentException("Invalid line: " + line); + } + condition.setTestString(tokenizer.nextToken()); + condition.setCondPattern(tokenizer.nextToken()); + if (tokenizer.hasMoreTokens()) { + String flags = tokenizer.nextToken(); + if (flags.startsWith("[") && flags.endsWith("]")) { + flags = flags.substring(1, flags.length() - 1); + } + StringTokenizer flagsTokenizer = new StringTokenizer(flags, ","); + while (flagsTokenizer.hasMoreElements()) { + parseCondFlag(line, condition, flagsTokenizer.nextToken()); + } + } + return condition; + } else if (token.equals("RewriteRule")) { + // RewriteRule Pattern Substitution [Flags] + RewriteRule rule = new RewriteRule(); + if (tokenizer.countTokens() < 2) { + throw new IllegalArgumentException("Invalid line: " + line); + } + rule.setPatternString(tokenizer.nextToken()); + rule.setSubstitutionString(tokenizer.nextToken()); + if (tokenizer.hasMoreTokens()) { + String flags = tokenizer.nextToken(); + if (flags.startsWith("[") && flags.endsWith("]")) { + flags = flags.substring(1, flags.length() - 1); + } + StringTokenizer flagsTokenizer = new StringTokenizer(flags, ","); + while (flagsTokenizer.hasMoreElements()) { + parseRuleFlag(line, rule, flagsTokenizer.nextToken()); + } + } + return rule; + } else if (token.equals("RewriteMap")) { + // RewriteMap name rewriteMapClassName whateverOptionalParameterInWhateverFormat + if (tokenizer.countTokens() < 2) { + throw new IllegalArgumentException("Invalid line: " + line); + } + String name = tokenizer.nextToken(); + String rewriteMapClassName = tokenizer.nextToken(); + RewriteMap map = null; + try { + map = (RewriteMap) (Class.forName(rewriteMapClassName).newInstance()); + } catch (Exception e) { + throw new IllegalArgumentException("Invalid map className: " + line); + } + if (tokenizer.hasMoreTokens()) { + map.setParameters(tokenizer.nextToken()); + } + Object[] result = new Object[2]; + result[0] = name; + result[1] = map; + return result; + } else if (token.startsWith("#")) { + // it's a comment, ignore it + } else { + throw new IllegalArgumentException("Invalid line: " + line); + } + } + return null; + } + + + /** + * Parser for RewriteCond flags. + * + * @param condition + * @param flag + */ + protected static void parseCondFlag(String line, RewriteCond condition, String flag) { + if (flag.equals("NC") || flag.equals("nocase")) { + condition.setNocase(true); + } else if (flag.equals("OR") || flag.equals("ornext")) { + condition.setOrnext(true); + } else { + throw new IllegalArgumentException("Invalid flag in: " + line + " flags: " + flag); + } + } + + + /** + * Parser for ReweriteRule flags. + * + * @param rule + * @param flag + */ + protected static void parseRuleFlag(String line, RewriteRule rule, String flag) { + if (flag.equals("chain") || flag.equals("C")) { + rule.setChain(true); + } else if (flag.startsWith("cookie=") || flag.startsWith("C=")) { + rule.setCookie(true); + if (flag.startsWith("cookie")) { + flag = flag.substring("cookie=".length()); + } else if (flag.startsWith("C=")) { + flag = flag.substring("C=".length()); + } + StringTokenizer tokenizer = new StringTokenizer(flag, ":"); + if (tokenizer.countTokens() < 2) { + throw new IllegalArgumentException("Invalid flag in: " + line); + } + rule.setCookieName(tokenizer.nextToken()); + rule.setCookieValue(tokenizer.nextToken()); + if (tokenizer.hasMoreTokens()) { + rule.setCookieDomain(tokenizer.nextToken()); + } + if (tokenizer.hasMoreTokens()) { + try { + rule.setCookieLifetime(Integer.parseInt(tokenizer.nextToken())); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Invalid flag in: " + line, e); + } + } + if (tokenizer.hasMoreTokens()) { + rule.setCookiePath(tokenizer.nextToken()); + } + if (tokenizer.hasMoreTokens()) { + rule.setCookieSecure(Boolean.parseBoolean(tokenizer.nextToken())); + } + if (tokenizer.hasMoreTokens()) { + rule.setCookieHttpOnly(Boolean.parseBoolean(tokenizer.nextToken())); + } + } else if (flag.startsWith("env=") || flag.startsWith("E=")) { + rule.setEnv(true); + if (flag.startsWith("env=")) { + flag = flag.substring("env=".length()); + } else if (flag.startsWith("E=")) { + flag = flag.substring("E=".length()); + } + int pos = flag.indexOf(':'); + if (pos == -1 || (pos + 1) == flag.length()) { + throw new IllegalArgumentException("Invalid flag in: " + line); + } + rule.addEnvName(flag.substring(0, pos)); + rule.addEnvValue(flag.substring(pos + 1)); + } else if (flag.startsWith("forbidden") || flag.startsWith("F")) { + rule.setForbidden(true); + } else if (flag.startsWith("gone") || flag.startsWith("G")) { + rule.setGone(true); + } else if (flag.startsWith("host") || flag.startsWith("H")) { + rule.setHost(true); + } else if (flag.startsWith("last") || flag.startsWith("L")) { + rule.setLast(true); + } else if (flag.startsWith("next") || flag.startsWith("N")) { + rule.setNext(true); + } else if (flag.startsWith("nocase") || flag.startsWith("NC")) { + rule.setNocase(true); + } else if (flag.startsWith("noescape") || flag.startsWith("NE")) { + rule.setNoescape(true); + } else if (flag.startsWith("proxy") || flag.startsWith("P")) { + // FIXME: Proxy not supported at the moment, would require proxy capabilities + //rule.setProxy(true); + } else if (flag.startsWith("qsappend") || flag.startsWith("QSA")) { + rule.setQsappend(true); + } else if (flag.startsWith("redirect") || flag.startsWith("R")) { + if (flag.startsWith("redirect=")) { + flag = flag.substring("redirect=".length()); + rule.setRedirect(true); + rule.setRedirectCode(Integer.parseInt(flag)); + } else if (flag.startsWith("R=")) { + flag = flag.substring("R=".length()); + rule.setRedirect(true); + rule.setRedirectCode(Integer.parseInt(flag)); + } else { + rule.setRedirect(true); + rule.setRedirectCode(HttpServletResponse.SC_FOUND); + } + } else if (flag.startsWith("skip") || flag.startsWith("S")) { + if (flag.startsWith("skip=")) { + flag = flag.substring("skip=".length()); + } else if (flag.startsWith("S=")) { + flag = flag.substring("S=".length()); + } + rule.setSkip(Integer.parseInt(flag)); + } else if (flag.startsWith("type") || flag.startsWith("T")) { + if (flag.startsWith("type=")) { + flag = flag.substring("type=".length()); + } else if (flag.startsWith("T=")) { + flag = flag.substring("T=".length()); + } + rule.setType(true); + rule.setTypeValue(flag); + } else { + throw new IllegalArgumentException("Invalid flag in: " + line + " flag: " + flag); + } + } + + + /** + * Determine if a URI string has a scheme component. + */ + protected static boolean hasScheme(StringBuffer uri) { + int len = uri.length(); + for(int i=0; i < len ; i++) { + char c = uri.charAt(i); + if(c == ':') { + return i > 0; + } else if(!URL.isSchemeChar(c)) { + return false; + } + } + return false; + } + +} Index: 3rdParty_sources/jbossweb/org/jboss/web/rewrite/Substitution.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/web/rewrite/Substitution.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/web/rewrite/Substitution.java 17 Aug 2012 14:43:39 -0000 1.1 @@ -0,0 +1,240 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2006, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + + +package org.jboss.web.rewrite; + +import java.util.ArrayList; +import java.util.Map; +import java.util.regex.Matcher; + +public class Substitution { + + public abstract class SubstitutionElement { + public abstract String evaluate(Matcher rule, Matcher cond, Resolver resolver); + } + + public class StaticElement extends SubstitutionElement { + public String value; + + public String evaluate + (Matcher rule, Matcher cond, Resolver resolver) { + return value; + } + + } + + public class RewriteRuleBackReferenceElement extends SubstitutionElement { + public int n; + public String evaluate(Matcher rule, Matcher cond, Resolver resolver) { + return rule.group(n); + } + } + + public class RewriteCondBackReferenceElement extends SubstitutionElement { + public int n; + public String evaluate(Matcher rule, Matcher cond, Resolver resolver) { + return cond.group(n); + } + } + + public class ServerVariableElement extends SubstitutionElement { + public String key; + public String evaluate(Matcher rule, Matcher cond, Resolver resolver) { + return resolver.resolve(key); + } + } + + public class ServerVariableEnvElement extends SubstitutionElement { + public String key; + public String evaluate(Matcher rule, Matcher cond, Resolver resolver) { + return resolver.resolveEnv(key); + } + } + + public class ServerVariableSslElement extends SubstitutionElement { + public String key; + public String evaluate(Matcher rule, Matcher cond, Resolver resolver) { + return resolver.resolveSsl(key); + } + } + + public class ServerVariableHttpElement extends SubstitutionElement { + public String key; + public String evaluate(Matcher rule, Matcher cond, Resolver resolver) { + return resolver.resolveHttp(key); + } + } + + public class MapElement extends SubstitutionElement { + public RewriteMap map = null; + public String key; + public String defaultValue = null; + public String evaluate(Matcher rule, Matcher cond, Resolver resolver) { + String result = map.lookup(key); + if (result == null) { + result = defaultValue; + } + return result; + } + } + + protected SubstitutionElement[] elements = null; + + protected String sub = null; + public String getSub() { return sub; } + public void setSub(String sub) { this.sub = sub; } + + public void parse(Map maps) { + + ArrayList elements = new ArrayList(); + int pos = 0; + int percentPos = 0; + int dollarPos = 0; + + while (pos < sub.length()) { + percentPos = sub.indexOf('%', pos); + dollarPos = sub.indexOf('$', pos); + if (percentPos == -1 && dollarPos == -1) { + // Static text + StaticElement newElement = new StaticElement(); + newElement.value = sub.substring(pos, sub.length()); + pos = sub.length(); + elements.add(newElement); + } else if (percentPos == -1 || ((dollarPos != -1) && (dollarPos < percentPos))) { + // $: back reference to rule or map lookup + if (dollarPos + 1 == sub.length()) { + throw new IllegalArgumentException(sub); + } + if (pos < dollarPos) { + // Static text + StaticElement newElement = new StaticElement(); + newElement.value = sub.substring(pos, dollarPos); + pos = dollarPos; + elements.add(newElement); + } + if (Character.isDigit(sub.charAt(dollarPos + 1))) { + // $: back reference to rule + RewriteRuleBackReferenceElement newElement = new RewriteRuleBackReferenceElement(); + newElement.n = Character.digit(sub.charAt(dollarPos + 1), 10); + pos = dollarPos + 2; + elements.add(newElement); + } else { + // $: map lookup as ${mapname:key|default} + MapElement newElement = new MapElement(); + int open = sub.indexOf('{', dollarPos); + int colon = sub.indexOf(':', dollarPos); + int def = sub.indexOf('|', dollarPos); + int close = sub.indexOf('}', dollarPos); + if (!(-1 < open && open < colon && colon < close)) { + throw new IllegalArgumentException(sub); + } + newElement.map = maps.get(sub.substring(open + 1, colon)); + if (newElement.map == null) { + throw new IllegalArgumentException(sub + ": No map: " + sub.substring(open + 1, colon)); + } + if (def > -1) { + if (!(colon < def && def < close)) { + throw new IllegalArgumentException(sub); + } + newElement.key = sub.substring(colon + 1, def); + newElement.defaultValue = sub.substring(def + 1, close); + } else { + newElement.key = sub.substring(colon + 1, close); + } + pos = close + 1; + elements.add(newElement); + } + } else { + // %: back reference to condition or server variable + if (percentPos + 1 == sub.length()) { + throw new IllegalArgumentException(sub); + } + if (pos < percentPos) { + // Static text + StaticElement newElement = new StaticElement(); + newElement.value = sub.substring(pos, percentPos); + pos = percentPos; + elements.add(newElement); + } + if (Character.isDigit(sub.charAt(percentPos + 1))) { + // %: back reference to condition + RewriteCondBackReferenceElement newElement = new RewriteCondBackReferenceElement(); + newElement.n = Character.digit(sub.charAt(percentPos + 1), 10); + pos = percentPos + 2; + elements.add(newElement); + } else { + // %: server variable as %{variable} + SubstitutionElement newElement = null; + int open = sub.indexOf('{', percentPos); + int colon = sub.indexOf(':', percentPos); + int close = sub.indexOf('}', percentPos); + if (!(-1 < open && open < close)) { + throw new IllegalArgumentException(sub); + } + if (colon > -1) { + if (!(open < colon && colon < close)) { + throw new IllegalArgumentException(sub); + } + String type = sub.substring(open + 1, colon); + if (type.equals("ENV")) { + newElement = new ServerVariableEnvElement(); + ((ServerVariableEnvElement) newElement).key = sub.substring(colon + 1, close); + } else if (type.equals("SSL")) { + newElement = new ServerVariableSslElement(); + ((ServerVariableSslElement) newElement).key = sub.substring(colon + 1, close); + } else if (type.equals("HTTP")) { + newElement = new ServerVariableHttpElement(); + ((ServerVariableHttpElement) newElement).key = sub.substring(colon + 1, close); + } else { + throw new IllegalArgumentException(sub + ": Bad type: " + type); + } + } else { + newElement = new ServerVariableElement(); + ((ServerVariableElement) newElement).key = sub.substring(open + 1, close); + } + pos = close + 1; + elements.add(newElement); + } + } + } + + this.elements = (SubstitutionElement[]) elements.toArray(new SubstitutionElement[0]); + + } + + /** + * Evaluate the substitution based on the context + * + * @param rule corresponding matched rule + * @param cond last matched condition + * @return + */ + public String evaluate(Matcher rule, Matcher cond, Resolver resolver) { + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < elements.length; i++) { + buf.append(elements[i].evaluate(rule, cond, resolver)); + } + return buf.toString(); + } + +} Index: 3rdParty_sources/jbossweb/org/jboss/web/rewrite/TomcatResolver.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/web/rewrite/TomcatResolver.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/web/rewrite/TomcatResolver.java 17 Aug 2012 14:43:39 -0000 1.1 @@ -0,0 +1,175 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2006, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + + +package org.jboss.web.rewrite; + +import java.util.Calendar; + +import org.apache.catalina.connector.Request; + +import org.apache.naming.resources.CacheEntry; +import org.apache.naming.resources.ProxyDirContext; +import org.apache.tomcat.util.http.FastHttpDateFormat; + +public class TomcatResolver extends Resolver { + + protected Request request = null; + + public TomcatResolver(Request request) { + this.request = request; + } + + /** + * The following are not implemented: + * - SERVER_ADMIN + * - API_VERSION + * - IS_SUBREQ + */ + public String resolve(String key) { + if (key.equals("HTTP_USER_AGENT")) { + return request.getHeader("user-agent"); + } else if (key.equals("HTTP_REFERER")) { + return request.getHeader("referer"); + } else if (key.equals("HTTP_COOKIE")) { + return request.getHeader("cookie"); + } else if (key.equals("HTTP_FORWARDED")) { + return request.getHeader("forwarded"); + } else if (key.equals("HTTP_HOST")) { + String host = request.getHeader("host"); + int index = host.indexOf(':'); + if (index != -1) + host = host.substring(0, index); + return host; + } else if (key.equals("HTTP_PROXY_CONNECTION")) { + return request.getHeader("proxy-connection"); + } else if (key.equals("HTTP_ACCEPT")) { + return request.getHeader("accept"); + } else if (key.equals("REMOTE_ADDR")) { + return request.getRemoteAddr(); + } else if (key.equals("REMOTE_HOST")) { + return request.getRemoteHost(); + } else if (key.equals("REMOTE_PORT")) { + return String.valueOf(request.getRemotePort()); + } else if (key.equals("REMOTE_USER")) { + return request.getRemoteUser(); + } else if (key.equals("REMOTE_IDENT")) { + return request.getRemoteUser(); + } else if (key.equals("REQUEST_METHOD")) { + return request.getMethod(); + } else if (key.equals("SCRIPT_FILENAME")) { + return request.getRealPath(request.getServletPath()); //FIXME ? + } else if (key.equals("REQUEST_PATH")) { + return request.getRequestPathMB().toString(); + } else if (key.equals("CONTEXT_PATH")) { + return request.getContextPath(); + } else if (key.equals("SERVLET_PATH")) { + return emptyStringIfNull(request.getServletPath()); + } else if (key.equals("PATH_INFO")) { + return emptyStringIfNull(request.getPathInfo()); + } else if (key.equals("QUERY_STRING")) { + return emptyStringIfNull(request.getQueryString()); + } else if (key.equals("AUTH_TYPE")) { + return request.getAuthType(); + } else if (key.equals("DOCUMENT_ROOT")) { + return request.getRealPath("/"); + } else if (key.equals("SERVER_NAME")) { + return request.getLocalName(); + } else if (key.equals("SERVER_ADDR")) { + return request.getLocalAddr(); + } else if (key.equals("SERVER_PORT")) { + return String.valueOf(request.getLocalPort()); + } else if (key.equals("SERVER_PROTOCOL")) { + return request.getProtocol(); + } else if (key.equals("SERVER_SOFTWARE")) { + return "tomcat"; + } else if (key.equals("THE_REQUEST")) { + return request.getMethod() + " " + request.getRequestURI() + + " " + request.getProtocol(); + } else if (key.equals("REQUEST_URI")) { + return request.getRequestURI(); + } else if (key.equals("REQUEST_FILENAME")) { + return request.getPathTranslated(); + } else if (key.equals("HTTPS")) { + return request.isSecure() ? "on" : "off"; + } else if (key.equals("TIME_YEAR")) { + return String.valueOf(Calendar.getInstance().get(Calendar.YEAR)); + } else if (key.equals("TIME_MON")) { + return String.valueOf(Calendar.getInstance().get(Calendar.MONTH)); + } else if (key.equals("TIME_DAY")) { + return String.valueOf(Calendar.getInstance().get(Calendar.DAY_OF_MONTH)); + } else if (key.equals("TIME_HOUR")) { + return String.valueOf(Calendar.getInstance().get(Calendar.HOUR_OF_DAY)); + } else if (key.equals("TIME_MIN")) { + return String.valueOf(Calendar.getInstance().get(Calendar.MINUTE)); + } else if (key.equals("TIME_SEC")) { + return String.valueOf(Calendar.getInstance().get(Calendar.SECOND)); + } else if (key.equals("TIME_WDAY")) { + return String.valueOf(Calendar.getInstance().get(Calendar.DAY_OF_WEEK)); + } else if (key.equals("TIME")) { + return FastHttpDateFormat.getCurrentDate(); + } + return null; + } + + public String resolveEnv(String key) { + return System.getProperty(key); + } + + public String resolveSsl(String key) { + // FIXME: Implement SSL environment variables + return null; + } + + public String resolveHttp(String key) { + return request.getHeader(key); + } + + public boolean resolveResource(int type, String name) { + ProxyDirContext resources = (ProxyDirContext) request.getContext().getResources(); + CacheEntry cacheEntry = resources.lookupCache(name); + if (!cacheEntry.exists) { + return false; + } else { + switch (type) { + case 0: + return (cacheEntry.resource == null); + case 1: + return (cacheEntry.resource != null); + case 2: + return (cacheEntry.resource != null + && cacheEntry.attributes.getContentLength() > 0); + default: + return false; + } + } + } + + private static final String emptyStringIfNull(String value) { + if (value == null) { + return ""; + } else { + return value; + } + } + +} Index: 3rdParty_sources/jbossweb/org/jboss/web/rewrite/mbeans-descriptors.xml =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jbossweb/org/jboss/web/rewrite/mbeans-descriptors.xml,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jbossweb/org/jboss/web/rewrite/mbeans-descriptors.xml 17 Aug 2012 14:43:39 -0000 1.1 @@ -0,0 +1,21 @@ + + + + + + + + + + + + Index: 3rdParty_sources/jexcelapi/common/Assert.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/common/Assert.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/common/Assert.java 17 Aug 2012 14:51:26 -0000 1.1 @@ -0,0 +1,61 @@ +/********************************************************************* +* +* Copyright (C) 2006 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package common; + +/** + * Simple assertion mechanism for use during development + */ +public final class Assert +{ + /** + * Throws an AssertionFailed exception if the specified condition is + * false + * + * @param condition The assertion condition which must be true + */ + public static void verify(boolean condition) + { + if (!condition) + { + throw new AssertionFailed(); + } + } + + /** + * If the condition evaluates to false, an AssertionFailed is thrown + * + * @param message A message thrown with the failed assertion + * @param condition If this evaluates to false, an AssertionFailed is thrown + */ + public static void verify(boolean condition, String message) + { + if (!condition) + { + throw new AssertionFailed(message); + } + } +} + + + + + + + Index: 3rdParty_sources/jexcelapi/common/AssertionFailed.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/common/AssertionFailed.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/common/AssertionFailed.java 17 Aug 2012 14:51:25 -0000 1.1 @@ -0,0 +1,48 @@ +/********************************************************************* +* +* Copyright (C) 2006 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + + +package common; + +/** + * An exception thrown when an assert (from the Assert class) fails + */ +public class AssertionFailed extends RuntimeException +{ + /** + * Default constructor + * Prints the stack trace + */ + public AssertionFailed() + { + super(); + printStackTrace(); + } + + /** + * Constructor with message + * Prints the stack trace + * + * @param s Message thrown with the assertion + */ + public AssertionFailed(String s) + { + super(s); + } +} Index: 3rdParty_sources/jexcelapi/common/BaseUnit.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/common/BaseUnit.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/common/BaseUnit.java 17 Aug 2012 14:51:26 -0000 1.1 @@ -0,0 +1,35 @@ +/********************************************************************* +* +* Copyright (C) 2006 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package common; + +public class BaseUnit +{ + private int index; + + protected BaseUnit(int ind) + { + index = ind; + } + + protected int getIndex() + { + return index; + } +} Index: 3rdParty_sources/jexcelapi/common/LengthConverter.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/common/LengthConverter.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/common/LengthConverter.java 17 Aug 2012 14:51:26 -0000 1.1 @@ -0,0 +1,60 @@ +/********************************************************************* +* +* Copyright (C) 2006 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package common; + +public class LengthConverter +{ + private static double[][] factors = + new double[LengthUnit.getCount()][LengthUnit.getCount()]; + + static + { + // The identity factors + factors[LengthUnit.POINTS.getIndex()][LengthUnit.POINTS.getIndex()] = 1; + factors[LengthUnit.METRES.getIndex()][LengthUnit.METRES.getIndex()] = 1; + factors[LengthUnit.CENTIMETRES.getIndex()][LengthUnit.CENTIMETRES.getIndex()] = 1; + factors[LengthUnit.INCHES.getIndex()][LengthUnit.INCHES.getIndex()] = 1; + + // The points conversion factors + factors[LengthUnit.POINTS.getIndex()][LengthUnit.METRES.getIndex()] = 0.00035277777778; + factors[LengthUnit.POINTS.getIndex()][LengthUnit.CENTIMETRES.getIndex()] = 0.035277777778; + factors[LengthUnit.POINTS.getIndex()][LengthUnit.INCHES.getIndex()] = 0.013888888889; + + // The metres conversion factors + factors[LengthUnit.METRES.getIndex()][LengthUnit.POINTS.getIndex()] = 2877.84; + factors[LengthUnit.METRES.getIndex()][LengthUnit.CENTIMETRES.getIndex()] = 100; + factors[LengthUnit.METRES.getIndex()][LengthUnit.INCHES.getIndex()] = 39.37; + + // The centimetres conversion factors + factors[LengthUnit.CENTIMETRES.getIndex()][LengthUnit.POINTS.getIndex()] = 28.34643; + factors[LengthUnit.CENTIMETRES.getIndex()][LengthUnit.METRES.getIndex()] = 0.01; + factors[LengthUnit.CENTIMETRES.getIndex()][LengthUnit.INCHES.getIndex()] = 0.3937; + + // The inches conversion factors + factors[LengthUnit.INCHES.getIndex()][LengthUnit.POINTS.getIndex()] = 72; + factors[LengthUnit.INCHES.getIndex()][LengthUnit.METRES.getIndex()] = 0.0254; + factors[LengthUnit.INCHES.getIndex()][LengthUnit.CENTIMETRES.getIndex()] = 2.54; + } + + public static double getConversionFactor(LengthUnit from, LengthUnit to) + { + return factors[from.getIndex()][to.getIndex()]; + } +} Index: 3rdParty_sources/jexcelapi/common/LengthUnit.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/common/LengthUnit.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/common/LengthUnit.java 17 Aug 2012 14:51:26 -0000 1.1 @@ -0,0 +1,44 @@ +/********************************************************************* +* +* Copyright (C) 2006 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package common; + + +/** + * Enumeration for units + */ +public class LengthUnit extends BaseUnit +{ + private static int count = 0; + + private LengthUnit() + { + super(count++); + } + + public static int getCount() + { + return count; + } + + public static LengthUnit POINTS = new LengthUnit(); + public static LengthUnit METRES = new LengthUnit(); + public static LengthUnit CENTIMETRES = new LengthUnit(); + public static LengthUnit INCHES = new LengthUnit(); +} Index: 3rdParty_sources/jexcelapi/common/Logger.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/common/Logger.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/common/Logger.java 17 Aug 2012 14:51:26 -0000 1.1 @@ -0,0 +1,173 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package common; + +import java.security.AccessControlException; + +/** + * Abstract wrapper class for the logging interface of choice. + * The methods declared here are the same as those for the log4j + */ +public abstract class Logger +{ + /** + * The singleton logger + */ + private static Logger logger = null; + + /** + * Factory method to return the logger + */ + public static final Logger getLogger(Class cl) + { + if (logger == null) + { + initializeLogger(); + } + + return logger.getLoggerImpl(cl); + } + + /** + * Initializes the logger in a thread safe manner + */ + private synchronized static void initializeLogger() + { + if (logger != null) + { + return; + } + + String loggerName = common.log.LoggerName.NAME; + + try + { + // First see if there was anything defined at run time + loggerName = System.getProperty("logger"); + + if (loggerName == null) + { + // Get the logger name from the compiled in logger + loggerName = common.log.LoggerName.NAME; + } + + logger = (Logger) Class.forName(loggerName).newInstance(); + } + catch(IllegalAccessException e) + { + logger = new common.log.SimpleLogger(); + logger.warn("Could not instantiate logger " + loggerName + + " using default"); + } + catch(InstantiationException e) + { + logger = new common.log.SimpleLogger(); + logger.warn("Could not instantiate logger " + loggerName + + " using default"); + } + catch (AccessControlException e) + { + logger = new common.log.SimpleLogger(); + logger.warn("Could not instantiate logger " + loggerName + + " using default"); + } + catch(ClassNotFoundException e) + { + logger = new common.log.SimpleLogger(); + logger.warn("Could not instantiate logger " + loggerName + + " using default"); + } + } + + /** + * Constructor + */ + protected Logger() + { + } + + /** + * Log a debug message + */ + public abstract void debug(Object message); + + /** + * Log a debug message and exception + */ + public abstract void debug(Object message, Throwable t); + + /** + * Log an error message + */ + public abstract void error(Object message); + + /** + * Log an error message object and exception + */ + public abstract void error(Object message, Throwable t); + + /** + * Log a fatal message + */ + public abstract void fatal(Object message); + + /** + * Log a fatal message and exception + */ + public abstract void fatal(Object message, Throwable t); + + /** + * Log an information message + */ + public abstract void info(Object message); + + /** + * Logs an information message and an exception + */ + public abstract void info(Object message, Throwable t); + + /** + * Log a warning message object + */ + public abstract void warn(Object message); + + /** + * Log a warning message with exception + */ + public abstract void warn(Object message, Throwable t); + + /** + * Accessor to the logger implementation + */ + protected abstract Logger getLoggerImpl(Class cl); + + /** + * Empty implementation of the suppressWarnings. Subclasses may + * or may not override this method. This method is included + * primarily for backwards support of the jxl.nowarnings property, and + * is used only by the SimpleLogger + * + * @param w suppression flag + */ + public void setSuppressWarnings(boolean w) + { + // default implementation does nothing + } +} Index: 3rdParty_sources/jexcelapi/common/log/Log4JLogger.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/common/log/Log4JLogger.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/common/log/Log4JLogger.java 17 Aug 2012 14:51:25 -0000 1.1 @@ -0,0 +1,142 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package common.log; + +import org.apache.log4j.Logger; + +/** + * A logger which uses the log4j library from jakarta. Each instance + * of this class acts as a wrapper to the log4j Logger class + */ +public class Log4JLogger extends common.Logger +{ + /** + * The log4j logger + */ + private Logger log4jLogger; + + /** + * Default constructor. This constructor is + */ + public Log4JLogger() + { + super(); + } + + /** + * Constructor invoked by the getLoggerImpl method to return a logger + * for a particular class + */ + private Log4JLogger(Logger l) + { + super(); + log4jLogger = l; + } + + /** + * Log a debug message + */ + public void debug(Object message) + { + log4jLogger.debug(message); + } + + /** + * Log a debug message and exception + */ + public void debug(Object message, Throwable t) + { + log4jLogger.debug(message, t); + } + + /** + * Log an error message + */ + public void error(Object message) + { + log4jLogger.error(message); + } + + /** + * Log an error message object and exception + */ + public void error(Object message, Throwable t) + { + log4jLogger.error(message, t); + } + + /** + * Log a fatal message + */ + public void fatal(Object message) + { + log4jLogger.fatal(message); + } + + /** + * Log a fatal message and exception + */ + public void fatal(Object message, Throwable t) + { + log4jLogger.fatal(message,t); + } + + /** + * Log an information message + */ + public void info(Object message) + { + log4jLogger.info(message); + } + + /** + * Logs an information message and an exception + */ + + public void info(Object message, Throwable t) + { + log4jLogger.info(message, t); + } + + /** + * Log a warning message object + */ + public void warn(Object message) + { + log4jLogger.warn(message); + } + + /** + * Log a warning message with exception + */ + public void warn(Object message, Throwable t) + { + log4jLogger.warn(message, t); + } + + /** + * Accessor to the logger implementation + */ + protected common.Logger getLoggerImpl(Class cl) + { + Logger l = Logger.getLogger(cl); + return new Log4JLogger(l); + } +} Index: 3rdParty_sources/jexcelapi/common/log/Log4jLoggerName.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/common/log/Log4jLoggerName.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/common/log/Log4jLoggerName.java 17 Aug 2012 14:51:25 -0000 1.1 @@ -0,0 +1,30 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package common.log; + +/** + * Static structure containing the class name of the logger. This may + * be overwritten at build time if loggers other than the default, + * no-dependency logger are required + */ +public class LoggerName +{ + public final static String NAME=common.log.Log4JLogger.class.getName(); +} Index: 3rdParty_sources/jexcelapi/common/log/LoggerName.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/common/log/LoggerName.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/common/log/LoggerName.java 17 Aug 2012 14:51:25 -0000 1.1 @@ -0,0 +1,30 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package common.log; + +/** + * Static structure containing the class name of the logger. This may + * be overwritten at build time if loggers other than the default, + * no-dependency logger are required + */ +public class LoggerName +{ + public final static String NAME=common.log.SimpleLogger.class.getName(); +} Index: 3rdParty_sources/jexcelapi/common/log/SimpleLogger.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/common/log/SimpleLogger.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/common/log/SimpleLogger.java 17 Aug 2012 14:51:25 -0000 1.1 @@ -0,0 +1,176 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package common.log; + +import common.Logger; + +/** + * The default logger. Simple writes everything out to stdout or stderr + */ +public class SimpleLogger extends Logger +{ + /** + * Flag to indicate whether or not warnings should be suppressed + */ + private boolean suppressWarnings; + + /** + * Constructor + */ + public SimpleLogger() + { + suppressWarnings = false; + } + + /** + * Log a debug message + */ + public void debug(Object message) + { + if (!suppressWarnings) + { + System.out.print("Debug: "); + System.out.println(message); + } + } + + /** + * Log a debug message and exception + */ + public void debug(Object message, Throwable t) + { + if (!suppressWarnings) + { + System.out.print("Debug: "); + System.out.println(message); + t.printStackTrace(); + } + } + + /** + * Log an error message + */ + public void error(Object message) + { + System.err.print("Error: "); + System.err.println(message); + } + + /** + * Log an error message object and exception + */ + public void error(Object message, Throwable t) + { + System.err.print("Error: "); + System.err.println(message); + t.printStackTrace(); + } + + /** + * Log a fatal message + */ + public void fatal(Object message) + { + System.err.print("Fatal: "); + System.err.println(message); + } + + /** + * Log a fatal message and exception + */ + public void fatal(Object message, Throwable t) + { + System.err.print("Fatal: "); + System.err.println(message); + t.printStackTrace(); + } + + /** + * Log an information message + */ + public void info(Object message) + { + if (!suppressWarnings) + { + System.out.println(message); + } + } + + /** + * Logs an information message and an exception + */ + + public void info(Object message, Throwable t) + { + if (!suppressWarnings) + { + System.out.println(message); + t.printStackTrace(); + } + } + + /** + * Log a warning message object + */ + public void warn(Object message) + { + if (!suppressWarnings) + { + System.err.print("Warning: "); + System.err.println(message); + } + } + + /** + * Log a warning message with exception + */ + public void warn(Object message, Throwable t) + { + if (!suppressWarnings) + { + System.err.print("Warning: "); + System.err.println(message); + t.printStackTrace(); + } + } + + /** + * Accessor to the logger implementation + */ + protected Logger getLoggerImpl(Class c) + { + return this; + } + + /** + * Overrides the method in the base class to suppress warnings - it can + * be set using the system property jxl.nowarnings. + * This method was originally present in the WorkbookSettings bean, + * but has been moved to the logger class. This means it is now present + * when the JVM is initialized, and subsequent to change it on + * a Workbook by Workbook basis will prove fruitless + * + * @param w suppression flag + */ + public void setSuppressWarnings(boolean w) + { + suppressWarnings = w; + } +} Index: 3rdParty_sources/jexcelapi/common/log/SimpleLoggerName.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/common/log/SimpleLoggerName.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/common/log/SimpleLoggerName.java 17 Aug 2012 14:51:25 -0000 1.1 @@ -0,0 +1,30 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package common.log; + +/** + * Static structure containing the class name of the logger. This may + * be overwritten at build time if loggers other than the default, + * no-dependency logger are required + */ +public class LoggerName +{ + public final static String NAME=common.log.SimpleLogger.class.getName(); +} Index: 3rdParty_sources/jexcelapi/jxl/BooleanCell.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/BooleanCell.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/BooleanCell.java 17 Aug 2012 14:51:24 -0000 1.1 @@ -0,0 +1,46 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl; + +/** + * This type represents the Microsoft concept of a Boolean. Accordingly, this + * cell represents either TRUE, FALSE or an error condition. This third + * state naturally makes handling BooleanCells quite tricky, and use of + * the specific access methods should be handled with care + */ +public interface BooleanCell extends Cell +{ + /** + * Gets the boolean value stored in this cell. If this cell contains an + * error, then returns FALSE. Always query this cell type using the + * accessor method isError() prior to calling this method + * + * @return TRUE if this cell contains TRUE, FALSE if it contains FALSE or + * an error code + */ + public boolean getValue(); +} + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/BooleanFormulaCell.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/BooleanFormulaCell.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/BooleanFormulaCell.java 17 Aug 2012 14:51:25 -0000 1.1 @@ -0,0 +1,28 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl; + +/** + * A mixin interface for numerical formulas, which combines the interfaces + * for formulas and for numbers + */ +public interface BooleanFormulaCell extends BooleanCell, FormulaCell +{ +} Index: 3rdParty_sources/jexcelapi/jxl/Cell.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/Cell.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/Cell.java 17 Aug 2012 14:51:25 -0000 1.1 @@ -0,0 +1,87 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl; + +import jxl.format.CellFormat; + +/** + * Represents an individual Cell within a Sheet. May be queried for its + * type and its content + */ +public interface Cell +{ + /** + * Returns the row number of this cell + * + * @return the row number of this cell + */ + public int getRow(); + + /** + * Returns the column number of this cell + * + * @return the column number of this cell + */ + public int getColumn(); + + /** + * Returns the content type of this cell + * + * @return the content type for this cell + */ + public CellType getType(); + + /** + * Indicates whether or not this cell is hidden, by virtue of either + * the entire row or column being collapsed + * + * @return TRUE if this cell is hidden, FALSE otherwise + */ + public boolean isHidden(); + + /** + * Quick and dirty function to return the contents of this cell as a string. + * For more complex manipulation of the contents, it is necessary to cast + * this interface to correct subinterface + * + * @return the contents of this cell as a string + */ + public String getContents(); + + /** + * Gets the cell format which applies to this cell + * Note that for cell with a cell type of EMPTY, which has no formatting + * information, this method will return null. Some empty cells (eg. on + * template spreadsheets) may have a cell type of EMPTY, but will + * actually contain formatting information + * + * @return the cell format applied to this cell, or NULL if this is an + * empty cell + */ + public CellFormat getCellFormat(); + + /** + * Gets any special cell features, such as comments (notes) or cell + * validation present for this cell + * + * @return the cell features, or NULL if this cell has no special features + */ + public CellFeatures getCellFeatures(); +} Index: 3rdParty_sources/jexcelapi/jxl/CellFeatures.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/CellFeatures.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/CellFeatures.java 17 Aug 2012 14:51:24 -0000 1.1 @@ -0,0 +1,67 @@ +/********************************************************************* +* +* Copyright (C) 2004 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl; + +import jxl.biff.BaseCellFeatures; + +/** + * Container for any additional cell features + */ +public class CellFeatures extends BaseCellFeatures +{ + /** + * Constructor + */ + public CellFeatures() + { + super(); + } + + /** + * Copy constructor + * + * @param cf cell to copy + */ + protected CellFeatures(CellFeatures cf) + { + super(cf); + } + + /** + * Accessor for the cell comment + * + * @return the cell comment, or NULL if this cell doesn't have + * a comment associated with it + */ + public String getComment() + { + return super.getComment(); + } + + /** + * Gets the data validation list + * + * @return the data validation list + */ + public String getDataValidationList() + { + return super.getDataValidationList(); + } +} Index: 3rdParty_sources/jexcelapi/jxl/CellFormat.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/CellFormat.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/CellFormat.java 17 Aug 2012 14:51:25 -0000 1.1 @@ -0,0 +1,29 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl; + +/** + * Interface for cell formats - used for typing information + * + * @deprecated Repackaged as jxl.format.CellFormat + */ +public interface CellFormat extends jxl.format.CellFormat +{ +} Index: 3rdParty_sources/jexcelapi/jxl/CellReferenceHelper.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/CellReferenceHelper.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/CellReferenceHelper.java 17 Aug 2012 14:51:25 -0000 1.1 @@ -0,0 +1,249 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl; + +import jxl.write.WritableWorkbook; +/** + * Exposes some cell reference helper methods to the public interface. + * This class merely delegates to the internally used reference helper + */ +public final class CellReferenceHelper +{ + /** + * Hide the default constructor + */ + private CellReferenceHelper() + { + } + + /** + * Appends the cell reference for the column and row passed in to the string + * buffer + * + * @param column the column + * @param row the row + * @param buf the string buffer to append + */ + public static void getCellReference(int column, int row, StringBuffer buf) + { + jxl.biff.CellReferenceHelper.getCellReference(column, row, buf); + } + + /** + * Overloaded method which prepends $ for absolute reference + * + * @param column the column number + * @param colabs TRUE if the column reference is absolute + * @param row the row number + * @param rowabs TRUE if the row reference is absolute + * @param buf the string buffer + */ + public static void getCellReference(int column, + boolean colabs, + int row, + boolean rowabs, + StringBuffer buf) + { + jxl.biff.CellReferenceHelper.getCellReference(column, colabs, + row, rowabs, + buf); + } + + + /** + * Gets the cell reference for the specified column and row + * + * @param column the column + * @param row the row + * @return the cell reference + */ + public static String getCellReference(int column, int row) + { + return jxl.biff.CellReferenceHelper.getCellReference(column, row); + } + + /** + * Gets the columnn number of the string cell reference + * + * @param s the string to parse + * @return the column portion of the cell reference + */ + public static int getColumn(String s) + { + return jxl.biff.CellReferenceHelper.getColumn(s); + } + + /** + * Gets the column letter corresponding to the 0-based column number + * + * @param c the column number + * @return the letter for that column number + */ + public static String getColumnReference(int c) + { + return jxl.biff.CellReferenceHelper.getColumnReference(c); + } + + /** + * Gets the row number of the cell reference + * @param s the cell reference + * @return the row number + */ + public static int getRow(String s) + { + return jxl.biff.CellReferenceHelper.getRow(s); + } + + /** + * Sees if the column component is relative or not + * + * @param s the cell + * @return TRUE if the column is relative, FALSE otherwise + */ + public static boolean isColumnRelative(String s) + { + return jxl.biff.CellReferenceHelper.isColumnRelative(s); + } + + /** + * Sees if the row component is relative or not + * + * @param s the cell + * @return TRUE if the row is relative, FALSE otherwise + */ + public static boolean isRowRelative(String s) + { + return jxl.biff.CellReferenceHelper.isRowRelative(s); + } + + /** + * Gets the fully qualified cell reference given the column, row + * external sheet reference etc + * + * @param sheet the sheet index + * @param column the column index + * @param row the row index + * @param workbook the workbook + * @param buf a string buffer + */ + public static void getCellReference + (int sheet, int column, int row, + Workbook workbook, StringBuffer buf) + { + jxl.biff.CellReferenceHelper.getCellReference + (sheet, column, row, (jxl.biff.formula.ExternalSheet) workbook, buf); + } + + /** + * Gets the fully qualified cell reference given the column, row + * external sheet reference etc + * + * @param sheet the sheet + * @param column the column + * @param row the row + * @param workbook the workbook + * @param buf the buffer + */ + public static void getCellReference(int sheet, + int column, + int row, + WritableWorkbook workbook, + StringBuffer buf) + { + jxl.biff.CellReferenceHelper.getCellReference + (sheet, column, row, (jxl.biff.formula.ExternalSheet) workbook, buf); + } + + /** + * Gets the fully qualified cell reference given the column, row + * external sheet reference etc + * + * @param sheet the sheet + * @param column the column + * @param colabs TRUE if the column is an absolute reference + * @param row the row + * @param rowabs TRUE if the row is an absolute reference + * @param workbook the workbook + * @param buf the string buffer + */ + public static void getCellReference (int sheet, + int column, + boolean colabs, + int row, + boolean rowabs, + Workbook workbook, + StringBuffer buf) + { + jxl.biff.CellReferenceHelper.getCellReference + (sheet, column, colabs, row, rowabs, + (jxl.biff.formula.ExternalSheet) workbook, buf); + } + + /** + * Gets the fully qualified cell reference given the column, row + * external sheet reference etc + * + * @param sheet the sheet + * @param column the column + * @param row the row + * @param workbook the workbook + * @return the cell reference in the form 'Sheet 1'!A1 + */ + public static String getCellReference (int sheet, + int column, + int row, + Workbook workbook) + { + return jxl.biff.CellReferenceHelper.getCellReference + (sheet, column, row, (jxl.biff.formula.ExternalSheet) workbook); + } + + /** + * Gets the fully qualified cell reference given the column, row + * external sheet reference etc + * + * @param sheet the sheet + * @param column the column + * @param row the row + * @param workbook the workbook + * @return the cell reference in the form 'Sheet 1'!A1 + */ + public static String getCellReference(int sheet, + int column, + int row, + WritableWorkbook workbook) + { + return jxl.biff.CellReferenceHelper.getCellReference + (sheet, column, row, (jxl.biff.formula.ExternalSheet) workbook); + } + + + /** + * Gets the sheet name from the cell reference string + * + * @param ref the cell reference + * @return the sheet name + */ + public static String getSheet(String ref) + { + return jxl.biff.CellReferenceHelper.getSheet(ref); + } + +} Index: 3rdParty_sources/jexcelapi/jxl/CellType.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/CellType.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/CellType.java 17 Aug 2012 14:51:24 -0000 1.1 @@ -0,0 +1,89 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl; + +/** + * An enumeration type listing the available content types for a cell + */ +public final class CellType +{ + + /** + * The text description of this cell type + */ + private String description; + + /** + * Private constructor + * @param desc the description of this type + */ + private CellType(String desc) + { + description = desc; + } + + /** + * Returns a string description of this cell + * + * @return the string description for this type + */ + public String toString() + { + return description; + } + + /** + */ + public static final CellType EMPTY = new CellType("Empty"); + /** + */ + public static final CellType LABEL = new CellType("Label"); + /** + */ + public static final CellType NUMBER = new CellType("Number"); + /** + */ + public static final CellType BOOLEAN = new CellType("Boolean"); + /** + */ + public static final CellType ERROR = new CellType("Error"); + /** + */ + public static final CellType NUMBER_FORMULA = + new CellType("Numerical Formula"); + /** + */ + public static final CellType DATE_FORMULA = new CellType("Date Formula"); + /** + */ + public static final CellType STRING_FORMULA = new CellType("String Formula"); + /** + */ + public static final CellType BOOLEAN_FORMULA = + new CellType("Boolean Formula"); + /** + */ + public static final CellType FORMULA_ERROR = new CellType("Formula Error"); + /** + */ + public static final CellType DATE = new CellType("Date"); +} + + Index: 3rdParty_sources/jexcelapi/jxl/CellView.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/CellView.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/CellView.java 17 Aug 2012 14:51:24 -0000 1.1 @@ -0,0 +1,210 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl; + +import jxl.format.CellFormat; + +/** + * This is a bean which client applications may use to get/set various + * properties for a row or column on a spreadsheet + */ +public final class CellView +{ + /** + * The dimension for the associated group of cells. For columns this + * will be width in characters, for rows this will be the + * height in points + * This attribute is deprecated in favour of the size attribute + */ + private int dimension; + + /** + * The size for the associated group of cells. For columns this + * will be width in characters multiplied by 256, for rows this will be the + * height in points + */ + private int size; + + /** + * Indicates whether the deprecated function was used to set the dimension + */ + private boolean depUsed; + + /** + * Indicates whether or not this sheet is hidden + */ + private boolean hidden; + + /** + * The cell format for the row/column + */ + private CellFormat format; + + /** + * Indicates that this column/row should be autosized + */ + private boolean autosize; + + /** + * Default constructor + */ + public CellView() + { + hidden = false; + depUsed = false; + dimension = 1; + size = 1; + autosize = false; + } + + /** + * Copy constructor + */ + public CellView(CellView cv) + { + hidden = cv.hidden; + depUsed = cv.depUsed; + dimension = cv.dimension; + size = cv.size; + autosize = cv.autosize; + } + + /** + * Sets the hidden status of this row/column + * + * @param h the hidden flag + */ + public void setHidden(boolean h) + { + hidden = h; + } + + /** + * Accessor for the hidden nature of this row/column + * + * @return TRUE if this row/column is hidden, FALSE otherwise + */ + public boolean isHidden() + { + return hidden; + } + + /** + * Sets the dimension for this view + * + * @param d the width of the column in characters, or the height of the + * row in 1/20ths of a point + * @deprecated use the setSize method instead + */ + public void setDimension(int d) + { + dimension = d; + depUsed = true; + } + + /** + * Sets the dimension for this view + * + * @param d the width of the column in characters multiplied by 256, + * or the height of the row in 1/20ths of a point + */ + public void setSize(int d) + { + size = d; + depUsed = false; + } + + /** + * Gets the width of the column in characters or the height of the + * row in 1/20ths + * + * @return the dimension + * @deprecated use getSize() instead + */ + public int getDimension() + { + return dimension; + } + + /** + * Gets the width of the column in characters multiplied by 256, or the + * height of the row in 1/20ths of a point + * + * @return the dimension + */ + public int getSize() + { + return size; + } + + /** + * Sets the cell format for this group of cells + * + * @param cf the format for every cell in the column/row + */ + public void setFormat(CellFormat cf) + { + format = cf; + } + + /** + * Accessor for the cell format for this group. + * + * @return the format for the column/row, or NULL if no format was + * specified + */ + public CellFormat getFormat() + { + return format; + } + + /** + * Accessor for the depUsed attribute + * + * @return TRUE if the deprecated methods were used to set the size, + * FALSE otherwise + */ + public boolean depUsed() + { + return depUsed; + } + + /** + * Sets the autosize flag. Currently, this only works for column views + * + * @param a autosize + */ + public void setAutosize(boolean a) + { + autosize = a; + } + + /** + * Accessor for the autosize flag + * NOTE: use of the autosize function is very processor intensive, so + * use with care + * + * @return TRUE if this row/column is to be autosized + */ + public boolean isAutosize() + { + return autosize; + } +} Index: 3rdParty_sources/jexcelapi/jxl/DateCell.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/DateCell.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/DateCell.java 17 Aug 2012 14:51:25 -0000 1.1 @@ -0,0 +1,54 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl; + +import java.text.DateFormat; +import java.util.Date; + +/** + * A date cell + */ +public interface DateCell extends Cell +{ + /** + * Gets the date contained in this cell + * + * @return the cell contents + */ + public Date getDate(); + + /** + * Indicates whether the date value contained in this cell refers to a date, + * or merely a time + * + * @return TRUE if the value refers to a time + */ + public boolean isTime(); + + /** + * Gets the DateFormat used to format the cell. This will normally be + * the format specified in the excel spreadsheet, but in the event of any + * difficulty parsing this, it will revert to the default date/time format. + * + * @return the DateFormat object used to format the date in the original + * excel cell + */ + public DateFormat getDateFormat(); +} Index: 3rdParty_sources/jexcelapi/jxl/DateFormulaCell.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/DateFormulaCell.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/DateFormulaCell.java 17 Aug 2012 14:51:24 -0000 1.1 @@ -0,0 +1,28 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl; + +/** + * A mixin interface for date formulas, which combines the interfaces + * for formulas and for dates + */ +public interface DateFormulaCell extends DateCell, FormulaCell +{ +} Index: 3rdParty_sources/jexcelapi/jxl/ErrorCell.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/ErrorCell.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/ErrorCell.java 17 Aug 2012 14:51:24 -0000 1.1 @@ -0,0 +1,37 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl; + +/** + * This type represents a cell which contains an error. This error will + * usually, but not always be the result of some error resulting from + * a formula + */ +public interface ErrorCell extends Cell +{ + /** + * Gets the error code for this cell. If this cell does not represent + * an error, then it returns 0. Always use the method isError() to + * determine this prior to calling this method + * + * @return the error code if this cell contains an error, 0 otherwise + */ + public int getErrorCode(); +} Index: 3rdParty_sources/jexcelapi/jxl/ErrorFormulaCell.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/ErrorFormulaCell.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/ErrorFormulaCell.java 17 Aug 2012 14:51:25 -0000 1.1 @@ -0,0 +1,28 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl; + +/** + * A mixin interface for numerical formulas, which combines the interfaces + * for formulas and for numbers + */ +public interface ErrorFormulaCell extends ErrorCell, FormulaCell +{ +} Index: 3rdParty_sources/jexcelapi/jxl/FormulaCell.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/FormulaCell.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/FormulaCell.java 17 Aug 2012 14:51:24 -0000 1.1 @@ -0,0 +1,36 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl; + +import jxl.biff.formula.FormulaException; + +/** + * Interface for formulas which allow clients to read the Excel formula + */ +public interface FormulaCell extends Cell +{ + /** + * Gets the formula as a string + * + * @return the formula as a string + * @exception FormulaException if an error occurred whilst parsing + */ + public String getFormula() throws FormulaException; +} Index: 3rdParty_sources/jexcelapi/jxl/HeaderFooter.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/HeaderFooter.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/HeaderFooter.java 17 Aug 2012 14:51:24 -0000 1.1 @@ -0,0 +1,380 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan, Eric Jung +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl; + +/** + * Class which represents an Excel header or footer. + */ +public final class HeaderFooter extends jxl.biff.HeaderFooter +{ + /** + * The contents - a simple wrapper around a string buffer + */ + public static class Contents extends jxl.biff.HeaderFooter.Contents + { + /** + * The constructor + */ + Contents() + { + super(); + } + + /** + * Constructor used when reading worksheets. The string contains all + * the formatting (but not alignment characters + * + * @param s the format string + */ + Contents(String s) + { + super(s); + } + + /** + * Copy constructor + * + * @param copy the contents to copy + */ + Contents(Contents copy) + { + super(copy); + } + + /** + * Appends the text to the string buffer + * + * @param txt the text to append + */ + public void append(String txt) + { + super.append(txt); + } + + /** + * Turns bold printing on or off. Bold printing + * is initially off. Text subsequently appended to + * this object will be bolded until this method is + * called again. + */ + public void toggleBold() + { + super.toggleBold(); + } + + /** + * Turns underline printing on or off. Underline printing + * is initially off. Text subsequently appended to + * this object will be underlined until this method is + * called again. + */ + public void toggleUnderline() + { + super.toggleUnderline(); + } + + /** + * Turns italics printing on or off. Italics printing + * is initially off. Text subsequently appended to + * this object will be italicized until this method is + * called again. + */ + public void toggleItalics() + { + super.toggleItalics(); + } + + /** + * Turns strikethrough printing on or off. Strikethrough printing + * is initially off. Text subsequently appended to + * this object will be striked out until this method is + * called again. + */ + public void toggleStrikethrough() + { + super.toggleStrikethrough(); + } + + /** + * Turns double-underline printing on or off. Double-underline printing + * is initially off. Text subsequently appended to + * this object will be double-underlined until this method is + * called again. + */ + public void toggleDoubleUnderline() + { + super.toggleDoubleUnderline(); + } + + /** + * Turns superscript printing on or off. Superscript printing + * is initially off. Text subsequently appended to + * this object will be superscripted until this method is + * called again. + */ + public void toggleSuperScript() + { + super.toggleSuperScript(); + } + + /** + * Turns subscript printing on or off. Subscript printing + * is initially off. Text subsequently appended to + * this object will be subscripted until this method is + * called again. + */ + public void toggleSubScript() + { + super.toggleSubScript(); + } + + /** + * Turns outline printing on or off (Macintosh only). + * Outline printing is initially off. Text subsequently appended + * to this object will be outlined until this method is + * called again. + */ + public void toggleOutline() + { + super.toggleOutline(); + } + + /** + * Turns shadow printing on or off (Macintosh only). + * Shadow printing is initially off. Text subsequently appended + * to this object will be shadowed until this method is + * called again. + */ + public void toggleShadow() + { + super.toggleShadow(); + } + + /** + * Sets the font of text subsequently appended to this + * object.. Previously appended text is not affected. + *

+ * Note: no checking is performed to + * determine if fontName is a valid font. + * + * @param fontName name of the font to use + */ + public void setFontName(String fontName) + { + super.setFontName(fontName); + } + + /** + * Sets the font size of text subsequently appended to this + * object. Previously appended text is not affected. + *

+ * Valid point sizes are between 1 and 99 (inclusive). If + * size is outside this range, this method returns false + * and does not change font size. If size is within this + * range, the font size is changed and true is returned. + * + * @param size The size in points. Valid point sizes are + * between 1 and 99 (inclusive). + * @return true if the font size was changed, false if font + * size was not changed because 1 > size > 99. + */ + public boolean setFontSize(int size) + { + return super.setFontSize(size); + } + + /** + * Appends the page number + */ + public void appendPageNumber() + { + super.appendPageNumber(); + } + + /** + * Appends the total number of pages + */ + public void appendTotalPages() + { + super.appendTotalPages(); + } + + /** + * Appends the current date + */ + public void appendDate() + { + super.appendDate(); + } + + /** + * Appends the current time + */ + public void appendTime() + { + super.appendTime(); + } + + /** + * Appends the workbook name + */ + public void appendWorkbookName() + { + super.appendWorkbookName(); + } + + /** + * Appends the worksheet name + */ + public void appendWorkSheetName() + { + super.appendWorkSheetName(); + } + + /** + * Clears the contents of this portion + */ + public void clear() + { + super.clear(); + } + + /** + * Queries if the contents are empty + * + * @return TRUE if the contents are empty, FALSE otherwise + */ + public boolean empty() + { + return super.empty(); + } + } + + /** + * Default constructor. + */ + public HeaderFooter() + { + super(); + } + + /** + * Copy constructor + * + * @param hf the item to copy + */ + public HeaderFooter(HeaderFooter hf) + { + super(hf); + } + + /** + * Constructor used when reading workbooks to separate the left, right + * a central part of the strings into their constituent parts + * + * @param s the header string + */ + public HeaderFooter(String s) + { + super(s); + } + + /** + * Retrieves a Stringified + * version of this object + * + * @return the header string + */ + public String toString() + { + return super.toString(); + } + + /** + * Accessor for the contents which appear on the right hand side of the page + * + * @return the right aligned contents + */ + public Contents getRight() + { + return (Contents) super.getRightText(); + } + + /** + * Accessor for the contents which in the centre of the page + * + * @return the centrally aligned contents + */ + public Contents getCentre() + { + return (Contents) super.getCentreText(); + } + + /** + * Accessor for the contents which appear on the left hand side of the page + * + * @return the left aligned contents + */ + public Contents getLeft() + { + return (Contents) super.getLeftText(); + } + + /** + * Clears the contents of the header/footer + */ + public void clear() + { + super.clear(); + } + + /** + * Creates internal class of the appropriate type + * + * @return the created contents + */ + protected jxl.biff.HeaderFooter.Contents createContents() + { + return new Contents(); + } + + /** + * Creates internal class of the appropriate type + * + * @param s the string to create the contents + * @return the created contents + */ + protected jxl.biff.HeaderFooter.Contents createContents(String s) + { + return new Contents(s); + } + + /** + * Creates internal class of the appropriate type + * + * @param c the contents to copy + * @return the new contents + */ + protected jxl.biff.HeaderFooter.Contents + createContents(jxl.biff.HeaderFooter.Contents c) + { + return new Contents((Contents) c); + } +} Index: 3rdParty_sources/jexcelapi/jxl/Hyperlink.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/Hyperlink.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/Hyperlink.java 17 Aug 2012 14:51:25 -0000 1.1 @@ -0,0 +1,107 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl; + +import java.io.File; +import java.net.URL; + +/** + * Hyperlink information. Only URLs or file links are supported + * + * Hyperlinks may apply to a range of cells; in such cases the methods + * getRow and getColumn return the cell at the top left of the range + * the hyperlink refers to. Hyperlinks have no specific cell format + * information applied to them, so the getCellFormat method will return null + */ +public interface Hyperlink +{ + /** + * Returns the row number of this cell + * + * @return the row number of this cell + */ + public int getRow(); + + /** + * Returns the column number of this cell + * + * @return the column number of this cell + */ + public int getColumn(); + + /** + * Gets the range of cells which activate this hyperlink + * The get sheet index methods will all return -1, because the + * cells will all be present on the same sheet + * + * @return the range of cells which activate the hyperlink + */ + public Range getRange(); + + /** + * Determines whether this is a hyperlink to a file + * + * @return TRUE if this is a hyperlink to a file, FALSE otherwise + */ + public boolean isFile(); + + /** + * Determines whether this is a hyperlink to a web resource + * + * @return TRUE if this is a URL + */ + public boolean isURL(); + + /** + * Determines whether this is a hyperlink to a location in this workbook + * + * @return TRUE if this is a link to an internal location + */ + public boolean isLocation(); + + /** + * Returns the row number of the bottom right cell + * + * @return the row number of this cell + */ + public int getLastRow(); + + /** + * Returns the column number of the bottom right cell + * + * @return the column number of this cell + */ + public int getLastColumn(); + + /** + * Gets the URL referenced by this Hyperlink + * + * @return the URL, or NULL if this hyperlink is not a URL + */ + public URL getURL(); + + /** + * Returns the local file eferenced by this Hyperlink + * + * @return the file, or NULL if this hyperlink is not a file + */ + public File getFile(); +} + Index: 3rdParty_sources/jexcelapi/jxl/Image.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/Image.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/Image.java 17 Aug 2012 14:51:24 -0000 1.1 @@ -0,0 +1,122 @@ +/********************************************************************* +* +* Copyright (C) 2004 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl; + +import common.LengthUnit; + +import java.io.File; + +/** + * Accessor functions for an image + */ +public interface Image +{ + /** + * Accessor for the image position + * + * @return the column number at which the image is positioned + */ + public double getColumn(); + + /** + * Accessor for the image position + * + * @return the row number at which the image is positioned + */ + public double getRow(); + + /** + * Accessor for the image dimensions + * + * @return the number of columns this image spans + */ + public double getWidth(); + + /** + * Accessor for the image dimensions + * + * @return the number of rows which this image spans + */ + public double getHeight(); + + /** + * Accessor for the image file + * + * @return the file which the image references + */ + public File getImageFile(); + + /** + * Accessor for the image data + * + * @return the image data + */ + public byte[] getImageData(); + + /** + * Get the width of this image as rendered within Excel + * + * @param unit the unit of measurement + * @return the width of the image within Excel + */ + public double getWidth(LengthUnit unit); + + /** + * Get the height of this image as rendered within Excel + * + * @param unit the unit of measurement + * @return the height of the image within Excel + */ + public double getHeight(LengthUnit unit); + + /** + * Gets the width of the image. Note that this is the width of the + * underlying image, and does not take into account any size manipulations + * that may have occurred when the image was added into Excel + * + * @return the image width in pixels + */ + public int getImageWidth(); + + /** + * Gets the height of the image. Note that this is the height of the + * underlying image, and does not take into account any size manipulations + * that may have occurred when the image was added into Excel + * + * @return the image height in pixels + */ + public int getImageHeight(); + + /** + * Gets the horizontal resolution of the image, if that information + * is available. + * + * @return the number of dots per unit specified, if available, 0 otherwise + */ + public double getHorizontalResolution(LengthUnit unit); + + /** + * Gets the vertical resolution of the image, if that information + * is available. + * + * @return the number of dots per unit specified, if available, 0 otherwise + */ + public double getVerticalResolution(LengthUnit unit); +} Index: 3rdParty_sources/jexcelapi/jxl/JXLException.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/JXLException.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/JXLException.java 17 Aug 2012 14:51:25 -0000 1.1 @@ -0,0 +1,36 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl; + +/** + * Base exception class for JExcelAPI exceptions + */ +public class JXLException extends Exception +{ + /** + * Constructor + * + * @param message the exception message + */ + protected JXLException(String message) + { + super(message); + } +} Index: 3rdParty_sources/jexcelapi/jxl/LabelCell.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/LabelCell.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/LabelCell.java 17 Aug 2012 14:51:24 -0000 1.1 @@ -0,0 +1,34 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl; + +/** + * A label cell + */ +public interface LabelCell extends Cell +{ + /** + * Gets the label for this cell. The value returned will be the same + * as for the getContents method in the base class + * + * @return the cell contents + */ + public String getString(); +} Index: 3rdParty_sources/jexcelapi/jxl/NumberCell.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/NumberCell.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/NumberCell.java 17 Aug 2012 14:51:25 -0000 1.1 @@ -0,0 +1,44 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl; + +import java.text.NumberFormat; + +/** + * A cell which contains a numerical value + */ +public interface NumberCell extends Cell +{ + /** + * Gets the double contents for this cell. + * + * @return the cell contents + */ + public double getValue(); + + /** + * Gets the NumberFormat used to format this cell. This is the java + * equivalent of the Excel format + * + * @return the NumberFormat used to format the cell + */ + public NumberFormat getNumberFormat(); +} + Index: 3rdParty_sources/jexcelapi/jxl/NumberFormulaCell.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/NumberFormulaCell.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/NumberFormulaCell.java 17 Aug 2012 14:51:24 -0000 1.1 @@ -0,0 +1,28 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl; + +/** + * A mixin interface for numerical formulas, which combines the interfaces + * for formulas and for numbers + */ +public interface NumberFormulaCell extends NumberCell, FormulaCell +{ +} Index: 3rdParty_sources/jexcelapi/jxl/Range.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/Range.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/Range.java 17 Aug 2012 14:51:25 -0000 1.1 @@ -0,0 +1,58 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl; + +/** + * Represents a 3-D range of cells in a workbook. This object is + * returned by the method findByName in a workbook + */ +public interface Range +{ + /** + * Gets the cell at the top left of this range + * + * @return the cell at the top left + */ + public Cell getTopLeft(); + + /** + * Gets the cell at the bottom right of this range + * + * @return the cell at the bottom right + */ + public Cell getBottomRight(); + + /** + * Gets the index of the first sheet in the range + * + * @return the index of the first sheet in the range + */ + public int getFirstSheetIndex(); + + /** + * Gets the index of the last sheet in the range + * + * @return the index of the last sheet in the range + */ + public int getLastSheetIndex(); +} + + + Index: 3rdParty_sources/jexcelapi/jxl/Sheet.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/Sheet.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/Sheet.java 17 Aug 2012 14:51:25 -0000 1.1 @@ -0,0 +1,279 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl; + +import java.util.regex.Pattern; +import jxl.format.CellFormat; + +/** + * Represents a sheet within a workbook. Provides a handle to the individual + * cells, or lines of cells (grouped by Row or Column) + */ +public interface Sheet +{ + /** + * Returns the cell specified at this row and at this column. + * If a column/row combination forms part of a merged group of cells + * then (unless it is the first cell of the group) a blank cell + * will be returned + * + * @param column the column number + * @param row the row number + * @return the cell at the specified co-ordinates + */ + public Cell getCell(int column, int row); + + /** + * Returns the cell for the specified location eg. "A4". Note that this + * method is identical to calling getCell(CellReferenceHelper.getColumn(loc), + * CellReferenceHelper.getRow(loc)) and its implicit performance + * overhead for string parsing. As such,this method should therefore + * be used sparingly + * + * @param loc the cell reference + * @return the cell at the specified co-ordinates + */ + public Cell getCell(String loc); + + /** + * Returns the number of rows in this sheet + * + * @return the number of rows in this sheet + */ + public int getRows(); + + /** + * Returns the number of columns in this sheet + * + * @return the number of columns in this sheet + */ + public int getColumns(); + + /** + * Gets all the cells on the specified row + * + * @param row the rows whose cells are to be returned + * @return the cells on the given row + */ + public Cell[] getRow(int row); + + /** + * Gets all the cells on the specified column + * + * @param col the column whose cells are to be returned + * @return the cells on the specified column + */ + public Cell[] getColumn(int col); + + /** + * Gets the name of this sheet + * + * @return the name of the sheet + */ + public String getName(); + + /** + * Determines whether the sheet is hidden + * + * @return whether or not the sheet is hidden + * @deprecated in favour of the getSettings() method + */ + public boolean isHidden(); + + /** + * Determines whether the sheet is protected + * + * @return whether or not the sheet is protected + * @deprecated in favour of the getSettings() method + */ + public boolean isProtected(); + + /** + * Gets the cell whose contents match the string passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform + * + * @param contents the string to match + * @return the Cell whose contents match the paramter, null if not found + */ + public Cell findCell(String contents); + + /** + * Gets the cell whose contents match the string passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform + * + * @param contents the string to match + * @param firstCol the first column within the range + * @param firstRow the first row of the range + * @param lastCol the last column within the range + * @param lastRow the last row within the range + * @param reverse indicates whether to perform a reverse search or not + * @return the Cell whose contents match the parameter, null if not found + */ + public Cell findCell(String contents, + int firstCol, + int firstRow, + int lastCol, + int lastRow, + boolean reverse); + + /** + * Gets the cell whose contents match the regular expressionstring passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform + * + * @param pattern the regular expression string to match + * @param firstCol the first column within the range + * @param firstRow the first row of the rang + * @param lastCol the last column within the range + * @param lastRow the last row within the range + * @param reverse indicates whether to perform a reverse search or not + * @return the Cell whose contents match the parameter, null if not found + */ + public Cell findCell(Pattern pattern, + int firstCol, + int firstRow, + int lastCol, + int lastRow, + boolean reverse); + + /** + * Gets the cell whose contents match the string passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform. This method differs + * from the findCell method in that only cells with labels are + * queried - all numerical cells are ignored. This should therefore + * improve performance. + * + * @param contents the string to match + * @return the Cell whose contents match the paramter, null if not found + */ + public LabelCell findLabelCell(String contents); + + /** + * Gets the hyperlinks on this sheet + * + * @return an array of hyperlinks + */ + public Hyperlink[] getHyperlinks(); + + /** + * Gets the cells which have been merged on this sheet + * + * @return an array of range objects + */ + public Range[] getMergedCells(); + + /** + * Gets the settings used on a particular sheet + * + * @return the sheet settings + */ + public SheetSettings getSettings(); + + /** + * Gets the column format for the specified column + * + * @param col the column number + * @return the column format, or NULL if the column has no specific format + * @deprecated Use getColumnView and the CellView bean instead + */ + public CellFormat getColumnFormat(int col); + + /** + * Gets the column width for the specified column + * + * @param col the column number + * @return the column width, or the default width if the column has no + * specified format + * @deprecated Use getColumnView instead + */ + public int getColumnWidth(int col); + + /** + * Gets the column width for the specified column + * + * @param col the column number + * @return the column format, or the default format if no override is + specified + */ + public CellView getColumnView(int col); + + /** + * Gets the row height for the specified column + * + * @param row the row number + * @return the row height, or the default height if the column has no + * specified format + * @deprecated use getRowView instead + */ + public int getRowHeight(int row); + + /** + * Gets the row height for the specified column + * + * @param row the row number + * @return the row format, which may be the default format if no format + * is specified + */ + public CellView getRowView(int row); + + /** + * Accessor for the number of images on the sheet + * + * @return the number of images on this sheet + */ + public int getNumberOfImages(); + + /** + * Accessor for the image + * + * @param i the 0 based image number + * @return the image at the specified position + */ + public Image getDrawing(int i); + + /** + * Accessor for the page breaks on this sheet + * + * @return the page breaks on this sheet + */ + public int[] getRowPageBreaks(); + + /** + * Accessor for the page breaks on this sheet + * + * @return the page breaks on this sheet + */ + public int[] getColumnPageBreaks(); + +} + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/SheetSettings.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/SheetSettings.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/SheetSettings.java 17 Aug 2012 14:51:24 -0000 1.1 @@ -0,0 +1,1344 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl; + +import common.Assert; + +import jxl.format.PageOrientation; +import jxl.format.PaperSize; +import jxl.biff.SheetRangeImpl; +import jxl.Range; +import jxl.format.PageOrder; + +/** + * This is a bean which client applications may use to get/set various + * properties which are associated with a particular worksheet, such + * as headers and footers, page orientation etc. + */ +public final class SheetSettings +{ + /** + * The page orientation + */ + private PageOrientation orientation; + + /** + * The page order + */ + private PageOrder pageOrder; + + /** + * The paper size for printing + */ + private PaperSize paperSize; + + /** + * Indicates whether or not this sheet is protected + */ + private boolean sheetProtected; + + /** + * Indicates whether or not this sheet is hidden + */ + private boolean hidden; + + /** + * Indicates whether or not this sheet is selected + */ + private boolean selected; + + /** + * The header + */ + private HeaderFooter header; + + /** + * The margin allocated for any page headers, in inches + */ + private double headerMargin; + + /** + * The footer + */ + private HeaderFooter footer; + + /** + * The margin allocated for any page footers, in inches + */ + private double footerMargin; + + /** + * The scale factor used when printing + */ + private int scaleFactor; + + /** + * The zoom factor used when viewing. Note the difference between + * this and the scaleFactor which is used when printing + */ + private int zoomFactor; + + /** + * The page number at which to commence printing + */ + private int pageStart; + + /** + * The number of pages into which this excel sheet is squeezed widthwise + */ + private int fitWidth; + + /** + * The number of pages into which this excel sheet is squeezed heightwise + */ + private int fitHeight; + + /** + * The horizontal print resolution + */ + private int horizontalPrintResolution; + + /** + * The vertical print resolution + */ + private int verticalPrintResolution; + + /** + * The margin from the left hand side of the paper in inches + */ + private double leftMargin; + + /** + * The margin from the right hand side of the paper in inches + */ + private double rightMargin; + + /** + * The margin from the top of the paper in inches + */ + private double topMargin; + + /** + * The margin from the bottom of the paper in inches + */ + private double bottomMargin; + + /** + * Indicates whether to fit the print to the pages or scale the output + * This field is manipulated indirectly by virtue of the setFitWidth/Height + * methods + */ + private boolean fitToPages; + + /** + * Indicates whether grid lines should be displayed + */ + private boolean showGridLines; + + /** + * Indicates whether grid lines should be printed + */ + private boolean printGridLines; + + /** + * Indicates whether sheet headings should be printed + */ + private boolean printHeaders; + + /** + * Indicates the view mode + */ + private boolean pageBreakPreviewMode; + + /** + * Indicates whether the sheet should display zero values + */ + private boolean displayZeroValues; + + /** + * The password for protected sheets + */ + private String password; + + /** + * The password hashcode - used when copying sheets + */ + private int passwordHash; + + /** + * The default column width, in characters + */ + private int defaultColumnWidth; + + /** + * The default row height, in 1/20th of a point + */ + private int defaultRowHeight; + + /** + * The horizontal freeze pane + */ + private int horizontalFreeze; + + /** + * The vertical freeze position + */ + private int verticalFreeze; + + /** + * Vertical centre flag + */ + private boolean verticalCentre; + + /** + * Horizontal centre flag + */ + private boolean horizontalCentre; + + /** + * The number of copies to print + */ + private int copies; + + /** + * Automatic formula calculation + */ + private boolean automaticFormulaCalculation; + + /** + * Recalculate the formulas before save + */ + private boolean recalculateFormulasBeforeSave; + + /** + * The magnification factor for use during page break preview mode (in + * percent) + */ + private int pageBreakPreviewMagnification; + + /** + * The magnification factor for use during normal mode (in percent) + */ + private int normalMagnification; + + /** + * The print area + */ + private Range printArea; + + /** + * The print row titles + */ + private Range printTitlesRow; + + /** + * The print column titles + */ + private Range printTitlesCol; + + /** + * A handle to the sheet - used internally for ranges + */ + private Sheet sheet; + + // *** + // The defaults + // ** + private static final PageOrientation DEFAULT_ORIENTATION = + PageOrientation.PORTRAIT; + private static final PageOrder DEFAULT_ORDER = + PageOrder.RIGHT_THEN_DOWN; + private static final PaperSize DEFAULT_PAPER_SIZE = PaperSize.A4; + private static final double DEFAULT_HEADER_MARGIN = 0.5; + private static final double DEFAULT_FOOTER_MARGIN = 0.5; + private static final int DEFAULT_PRINT_RESOLUTION = 0x12c; + private static final double DEFAULT_WIDTH_MARGIN = 0.75; + private static final double DEFAULT_HEIGHT_MARGIN = 1; + + private static final int DEFAULT_DEFAULT_COLUMN_WIDTH = 8; + private static final int DEFAULT_ZOOM_FACTOR = 100; + private static final int DEFAULT_NORMAL_MAGNIFICATION = 100; + private static final int DEFAULT_PAGE_BREAK_PREVIEW_MAGNIFICATION = 60; + + // The publicly accessible values + /** + * The default value for the default row height + */ + public static final int DEFAULT_DEFAULT_ROW_HEIGHT = 0xff; + + /** + * Default constructor + */ + public SheetSettings(Sheet s) + { + sheet = s; // for internal use, when accessing ranges + orientation = DEFAULT_ORIENTATION; + pageOrder = DEFAULT_ORDER; + paperSize = DEFAULT_PAPER_SIZE; + sheetProtected = false; + hidden = false; + selected = false; + headerMargin = DEFAULT_HEADER_MARGIN; + footerMargin = DEFAULT_FOOTER_MARGIN; + horizontalPrintResolution = DEFAULT_PRINT_RESOLUTION; + verticalPrintResolution = DEFAULT_PRINT_RESOLUTION; + leftMargin = DEFAULT_WIDTH_MARGIN; + rightMargin = DEFAULT_WIDTH_MARGIN; + topMargin = DEFAULT_HEIGHT_MARGIN; + bottomMargin = DEFAULT_HEIGHT_MARGIN; + fitToPages = false; + showGridLines = true; + printGridLines = false; + printHeaders = false; + pageBreakPreviewMode = false; + displayZeroValues = true; + defaultColumnWidth = DEFAULT_DEFAULT_COLUMN_WIDTH; + defaultRowHeight = DEFAULT_DEFAULT_ROW_HEIGHT; + zoomFactor = DEFAULT_ZOOM_FACTOR; + pageBreakPreviewMagnification = DEFAULT_PAGE_BREAK_PREVIEW_MAGNIFICATION; + normalMagnification = DEFAULT_NORMAL_MAGNIFICATION; + horizontalFreeze = 0; + verticalFreeze = 0; + copies = 1; + header = new HeaderFooter(); + footer = new HeaderFooter(); + automaticFormulaCalculation = true; + recalculateFormulasBeforeSave = true; + } + + /** + * Copy constructor. Called when copying sheets + * @param copy the settings to copy + */ + public SheetSettings(SheetSettings copy, Sheet s) + { + Assert.verify(copy != null); + + sheet = s; // for internal use when accessing ranges + orientation = copy.orientation; + pageOrder = copy.pageOrder; + paperSize = copy.paperSize; + sheetProtected = copy.sheetProtected; + hidden = copy.hidden; + selected = false; // don't copy the selected flag + headerMargin = copy.headerMargin; + footerMargin = copy.footerMargin; + scaleFactor = copy.scaleFactor; + pageStart = copy.pageStart; + fitWidth = copy.fitWidth; + fitHeight = copy.fitHeight; + horizontalPrintResolution = copy.horizontalPrintResolution; + verticalPrintResolution = copy.verticalPrintResolution; + leftMargin = copy.leftMargin; + rightMargin = copy.rightMargin; + topMargin = copy.topMargin; + bottomMargin = copy.bottomMargin; + fitToPages = copy.fitToPages; + password = copy.password; + passwordHash = copy.passwordHash; + defaultColumnWidth = copy.defaultColumnWidth; + defaultRowHeight = copy.defaultRowHeight; + zoomFactor = copy.zoomFactor; + pageBreakPreviewMagnification = copy.pageBreakPreviewMagnification; + normalMagnification = copy.normalMagnification; + showGridLines = copy.showGridLines; + displayZeroValues = copy.displayZeroValues; + pageBreakPreviewMode = copy.pageBreakPreviewMode; + horizontalFreeze = copy.horizontalFreeze; + verticalFreeze = copy.verticalFreeze; + horizontalCentre = copy.horizontalCentre; + verticalCentre = copy.verticalCentre; + copies = copy.copies; + header = new HeaderFooter(copy.header); + footer = new HeaderFooter(copy.footer); + automaticFormulaCalculation = copy.automaticFormulaCalculation; + recalculateFormulasBeforeSave = copy.recalculateFormulasBeforeSave; + + if (copy.printArea != null) + { + printArea = new SheetRangeImpl + (sheet, + copy.getPrintArea().getTopLeft().getColumn(), + copy.getPrintArea().getTopLeft().getRow(), + copy.getPrintArea().getBottomRight().getColumn(), + copy.getPrintArea().getBottomRight().getRow()); + } + + if (copy.printTitlesRow != null) + { + printTitlesRow = new SheetRangeImpl + (sheet, + copy.getPrintTitlesRow().getTopLeft().getColumn(), + copy.getPrintTitlesRow().getTopLeft().getRow(), + copy.getPrintTitlesRow().getBottomRight().getColumn(), + copy.getPrintTitlesRow().getBottomRight().getRow()); + } + + if (copy.printTitlesCol != null) + { + printTitlesCol = new SheetRangeImpl + (sheet, + copy.getPrintTitlesCol().getTopLeft().getColumn(), + copy.getPrintTitlesCol().getTopLeft().getRow(), + copy.getPrintTitlesCol().getBottomRight().getColumn(), + copy.getPrintTitlesCol().getBottomRight().getRow()); + } + } + + /** + * Sets the paper orientation for printing this sheet + * + * @param po the orientation + */ + public void setOrientation(PageOrientation po) + { + orientation = po; + } + + /** + * Accessor for the orientation + * + * @return the orientation + */ + public PageOrientation getOrientation() + { + return orientation; + } + + /** + * Accessor for the order + * + * @return + */ + public PageOrder getPageOrder() + { + return pageOrder; + } + + /** + * Sets the page order for printing this sheet + * + * @param order + */ + public void setPageOrder(PageOrder order) + { + this.pageOrder = order; + } + + /** + * Sets the paper size to be used when printing this sheet + * + * @param ps the paper size + */ + public void setPaperSize(PaperSize ps) + { + paperSize = ps; + } + + /** + * Accessor for the paper size + * + * @return the paper size + */ + public PaperSize getPaperSize() + { + return paperSize; + } + + /** + * Queries whether this sheet is protected (ie. read only) + * + * @return TRUE if this sheet is read only, FALSE otherwise + */ + public boolean isProtected() + { + return sheetProtected; + } + + /** + * Sets the protected (ie. read only) status of this sheet + * + * @param p the protected status + */ + public void setProtected(boolean p) + { + sheetProtected = p; + } + + /** + * Sets the margin for any page headers + * + * @param d the margin in inches + */ + public void setHeaderMargin(double d) + { + headerMargin = d; + } + + /** + * Accessor for the header margin + * + * @return the header margin + */ + public double getHeaderMargin() + { + return headerMargin; + } + + /** + * Sets the margin for any page footer + * + * @param d the footer margin in inches + */ + public void setFooterMargin(double d) + { + footerMargin = d; + } + + /** + * Accessor for the footer margin + * + * @return the footer margin + */ + public double getFooterMargin() + { + return footerMargin; + } + + /** + * Sets the hidden status of this worksheet + * + * @param h the hidden flag + */ + public void setHidden(boolean h) + { + hidden = h; + } + + /** + * Accessor for the hidden nature of this sheet + * + * @return TRUE if this sheet is hidden, FALSE otherwise + */ + public boolean isHidden() + { + return hidden; + } + + /** + * Sets this sheet to be when it is opened in excel + * + * @deprecated use overloaded version which takes a boolean + */ + public void setSelected() + { + setSelected(true); + } + + /** + * Sets this sheet to be when it is opened in excel + * + * @param s sets whether this sheet is selected or not + */ + public void setSelected(boolean s) + { + selected = s; + } + + /** + * Accessor for the selected nature of the sheet + * + * @return TRUE if this sheet is selected, FALSE otherwise + */ + public boolean isSelected() + { + return selected; + } + + /** + * Sets the scale factor for this sheet to be used when printing. The + * parameter is a percentage, therefore setting a scale factor of 100 will + * print at normal size, 50 half size, 200 double size etc + * + * @param sf the scale factor as a percentage + */ + public void setScaleFactor(int sf) + { + scaleFactor = sf; + fitToPages = false; + } + + /** + * Accessor for the scale factor + * + * @return the scale factor + */ + public int getScaleFactor() + { + return scaleFactor; + } + + /** + * Sets the page number at which to commence printing + * + * @param ps the page start number + */ + public void setPageStart(int ps) + { + pageStart = ps; + } + + /** + * Accessor for the page start + * + * @return the page start + */ + public int getPageStart() + { + return pageStart; + } + + /** + * Sets the number of pages widthwise which this sheet should be + * printed into + * + * @param fw the number of pages + */ + public void setFitWidth(int fw) + { + fitWidth = fw; + fitToPages = true; + } + + /** + * Accessor for the fit width + * + * @return the number of pages this sheet will be printed into widthwise + */ + public int getFitWidth() + { + return fitWidth; + } + + /** + * Sets the number of pages vertically that this sheet will be printed into + * + * @param fh the number of pages this sheet will be printed into heightwise + */ + public void setFitHeight(int fh) + { + fitHeight = fh; + fitToPages = true; + } + + /** + * Accessor for the fit height + * + * @return the number of pages this sheet will be printed into heightwise + */ + public int getFitHeight() + { + return fitHeight; + } + + /** + * Sets the horizontal print resolution + * + * @param hpw the print resolution + */ + public void setHorizontalPrintResolution(int hpw) + { + horizontalPrintResolution = hpw; + } + + /** + * Accessor for the horizontal print resolution + * + * @return the horizontal print resolution + */ + public int getHorizontalPrintResolution() + { + return horizontalPrintResolution; + } + + /** + * Sets the vertical print reslution + * + * @param vpw the vertical print resolution + */ + public void setVerticalPrintResolution(int vpw) + { + verticalPrintResolution = vpw; + } + + /** + * Accessor for the vertical print resolution + * + * @return the vertical print resolution + */ + public int getVerticalPrintResolution() + { + return verticalPrintResolution; + } + + /** + * Sets the right margin + * + * @param m the right margin in inches + */ + public void setRightMargin(double m) + { + rightMargin = m; + } + + /** + * Accessor for the right margin + * + * @return the right margin in inches + */ + public double getRightMargin() + { + return rightMargin; + } + + /** + * Sets the left margin + * + * @param m the left margin in inches + */ + public void setLeftMargin(double m) + { + leftMargin = m; + } + + /** + * Accessor for the left margin + * + * @return the left margin in inches + */ + public double getLeftMargin() + { + return leftMargin; + } + + /** + * Sets the top margin + * + * @param m the top margin in inches + */ + public void setTopMargin(double m) + { + topMargin = m; + } + + /** + * Accessor for the top margin + * + * @return the top margin in inches + */ + public double getTopMargin() + { + return topMargin; + } + + /** + * Sets the bottom margin + * + * @param m the bottom margin in inches + */ + public void setBottomMargin(double m) + { + bottomMargin = m; + } + + /** + * Accessor for the bottom margin + * + * @return the bottom margin in inches + */ + public double getBottomMargin() + { + return bottomMargin; + } + + /** + * Gets the default margin width + * + * @return the default margin width + */ + public double getDefaultWidthMargin() + { + return DEFAULT_WIDTH_MARGIN; + } + + /** + * Gets the default margin height + * + * @return the default margin height + */ + public double getDefaultHeightMargin() + { + return DEFAULT_HEIGHT_MARGIN; + } + + /** + * Accessor for the fit width print flag + * @return TRUE if the print is to fit to pages, false otherwise + */ + public boolean getFitToPages() + { + return fitToPages; + } + + /** + * Accessor for the fit to pages flag + * @param b TRUE to fit to pages, FALSE to use a scale factor + */ + public void setFitToPages(boolean b) + { + fitToPages = b; + } + + /** + * Accessor for the password + * + * @return the password to unlock this sheet, or NULL if not protected + */ + public String getPassword() + { + return password; + } + + /** + * Sets the password for this sheet + * + * @param s the password + */ + public void setPassword(String s) + { + password = s; + } + + /** + * Accessor for the password hash - used only when copying sheets + * + * @return passwordHash + */ + public int getPasswordHash() + { + return passwordHash; + } + + /** + * Accessor for the password hash - used only when copying sheets + * + * @param ph the password hash + */ + public void setPasswordHash(int ph) + { + passwordHash = ph; + } + + /** + * Accessor for the default column width + * + * @return the default column width, in characters + */ + public int getDefaultColumnWidth() + { + return defaultColumnWidth; + } + + /** + * Sets the default column width + * + * @param w the new default column width + */ + public void setDefaultColumnWidth(int w) + { + defaultColumnWidth = w; + } + + /** + * Accessor for the default row height + * + * @return the default row height, in 1/20ths of a point + */ + public int getDefaultRowHeight() + { + return defaultRowHeight; + } + + /** + * Sets the default row height + * + * @param h the default row height, in 1/20ths of a point + */ + public void setDefaultRowHeight(int h) + { + defaultRowHeight = h; + } + + /** + * Accessor for the zoom factor. Do not confuse zoom factor (which relates + * to the on screen view) with scale factor (which refers to the scale factor + * when printing) + * + * @return the zoom factor as a percentage + */ + public int getZoomFactor() + { + return zoomFactor; + } + + /** + * Sets the zoom factor. Do not confuse zoom factor (which relates + * to the on screen view) with scale factor (which refers to the scale factor + * when printing) + * + * @param zf the zoom factor as a percentage + */ + public void setZoomFactor(int zf) + { + zoomFactor = zf; + } + + /** + * Accessor for the page break preview mangificaton factor. + * Do not confuse zoom factor or scale factor + * + * @return the page break preview magnification a percentage + */ + public int getPageBreakPreviewMagnification() + { + return pageBreakPreviewMagnification; + } + + /** + * Accessor for the page break preview magnificaton factor. + * Do not confuse zoom factor or scale factor + * + * @param f the page break preview magnification as a percentage + */ + public void setPageBreakPreviewMagnification(int f) + { + pageBreakPreviewMagnification =f ; + } + + /** + * Accessor for the nomral view magnificaton factor. + * Do not confuse zoom factor or scale factor + * + * @return the page break preview magnification a percentage + */ + public int getNormalMagnification() + { + return normalMagnification; + } + + /** + * Accessor for the normal magnificaton factor. + * Do not confuse zoom factor or scale factor + * + * @param f the page break preview magnification as a percentage + */ + public void setNormalMagnification(int f) + { + normalMagnification = f ; + } + + + /** + * Accessor for the displayZeroValues property + * + * @return TRUE to display zero values, FALSE not to bother + */ + public boolean getDisplayZeroValues() + { + return displayZeroValues; + } + + /** + * Sets the displayZeroValues property + * + * @param b TRUE to show zero values, FALSE not to bother + */ + public void setDisplayZeroValues(boolean b) + { + displayZeroValues = b; + } + + /** + * Accessor for the showGridLines property + * + * @return TRUE if grid lines will be shown, FALSE otherwise + */ + public boolean getShowGridLines() + { + return showGridLines; + } + + /** + * Sets the showGridLines property + * + * @param b TRUE to show grid lines on this sheet, FALSE otherwise + */ + public void setShowGridLines(boolean b) + { + showGridLines = b; + } + + /** + * Accessor for the pageBreakPreview mode + * + * @return TRUE if page break preview is enabled, FALSE otherwise + */ + public boolean getPageBreakPreviewMode() + { + return pageBreakPreviewMode; + } + + /** + * Sets the pageBreakPreviewMode property + * + * @param b TRUE to launch in page break preview mode, FALSE otherwise + */ + public void setPageBreakPreviewMode(boolean b) + { + pageBreakPreviewMode = b; + } + + /** + * Accessor for the printGridLines property + * + * @return TRUE if grid lines will be printed, FALSE otherwise + */ + public boolean getPrintGridLines() + { + return printGridLines; + } + + /** + * Sets the printGridLines property + * + * @param b TRUE to print grid lines on this sheet, FALSE otherwise + */ + public void setPrintGridLines(boolean b) + { + printGridLines = b; + } + + /** + * Accessor for the printHeaders property + * + * @return TRUE if headers will be printed, FALSE otherwise + */ + public boolean getPrintHeaders() + { + return printHeaders; + } + + /** + * Sets the printHeaders property + * + * @param b TRUE to print headers on this sheet, FALSE otherwise + */ + public void setPrintHeaders(boolean b) + { + printHeaders = b; + } + + /** + * Gets the row at which the pane is frozen horizontally + * + * @return the row at which the pane is horizontally frozen, or 0 if there + * is no freeze + */ + public int getHorizontalFreeze() + { + return horizontalFreeze; + } + + /** + * Sets the row at which the pane is frozen horizontally + * + * @param row the row number to freeze at + */ + public void setHorizontalFreeze(int row) + { + horizontalFreeze = Math.max(row, 0); + } + + /** + * Gets the column at which the pane is frozen vertically + * + * @return the column at which the pane is vertically frozen, or 0 if there + * is no freeze + */ + public int getVerticalFreeze() + { + return verticalFreeze; + } + + /** + * Sets the row at which the pane is frozen vertically + * + * @param col the column number to freeze at + */ + public void setVerticalFreeze(int col) + { + verticalFreeze = Math.max(col, 0); + } + + /** + * Sets the number of copies + * + * @param c the number of copies + */ + public void setCopies(int c) + { + copies = c; + } + + /** + * Accessor for the number of copies to print + * + * @return the number of copies + */ + public int getCopies() + { + return copies; + } + + /** + * Accessor for the header + * + * @return the header + */ + public HeaderFooter getHeader() + { + return header; + } + + /** + * Sets the header + * + * @param h the header + */ + public void setHeader(HeaderFooter h) + { + header = h; + } + + /** + * Sets the footer + * + * @param f the footer + */ + public void setFooter(HeaderFooter f) + { + footer = f; + } + + /** + * Accessor for the footer + * + * @return the footer + */ + public HeaderFooter getFooter() + { + return footer; + } + + /** + * Accessor for the horizontal centre + * + * @return Returns the horizontalCentre. + */ + public boolean isHorizontalCentre() + { + return horizontalCentre; + } + + /** + * Sets the horizontal centre + * + * @param horizCentre The horizontalCentre to set. + */ + public void setHorizontalCentre(boolean horizCentre) + { + this.horizontalCentre = horizCentre; + } + + /** + * Accessor for the vertical centre + * + * @return Returns the verticalCentre. + */ + public boolean isVerticalCentre() + { + return verticalCentre; + } + + /** + * Sets the vertical centre + * + * @param vertCentre The verticalCentre to set. + */ + public void setVerticalCentre(boolean vertCentre) + { + this.verticalCentre = vertCentre; + } + + /** + * Sets the automatic formula calculation flag + * + * @param auto - TRUE to automatically calculate the formulas, + * FALSE otherwise + */ + public void setAutomaticFormulaCalculation(boolean auto) + { + automaticFormulaCalculation = auto; + } + + /** + * Retrieves the automatic formula calculation flag + * + * @return TRUE if formulas are calculated automatically, FALSE if they + * are calculated manually + */ + public boolean getAutomaticFormulaCalculation() + { + return automaticFormulaCalculation; + } + + /** + * Sets the recalculate formulas when the sheet is saved flag + * + * @param recalc - TRUE to automatically calculate the formulas when the, + * spreadsheet is saved, FALSE otherwise + */ + public void setRecalculateFormulasBeforeSave(boolean recalc) + { + recalculateFormulasBeforeSave = recalc; + } + + /** + * Retrieves the recalculate formulas before save flag + * + * @return TRUE if formulas are calculated before the sheet is saved, + * FALSE otherwise + */ + public boolean getRecalculateFormulasBeforeSave() + { + return recalculateFormulasBeforeSave; + } + + /** + * Sets the print area for this sheet + * + * @param firstCol the first column of the print area + * @param firstRow the first row of the print area + * @param lastCol the last column of the print area + * @param lastRow the last row of the print area + */ + public void setPrintArea(int firstCol, + int firstRow, + int lastCol, + int lastRow) + { + printArea = new SheetRangeImpl(sheet, firstCol, firstRow, + lastCol, lastRow); + } + + /** + * Accessor for the print area + * + * @return the print area, or NULL if one is not defined for this sheet + */ + public Range getPrintArea() + { + return printArea; + } + + /** + * Sets both of the print titles for this sheet + * + * @param firstRow the first row of the print row titles + * @param lastRow the last row of the print row titles + * @param firstCol the first column of the print column titles + * @param lastCol the last column of the print column titles + */ + public void setPrintTitles(int firstRow, + int lastRow, + int firstCol, + int lastCol) + { + setPrintTitlesRow(firstRow, lastRow); + setPrintTitlesCol(firstCol, lastCol); + } + + /** + * Sets the print row titles for this sheet + * + * @param firstRow the first row of the print titles + * @param lastRow the last row of the print titles + */ + public void setPrintTitlesRow(int firstRow, + int lastRow) + { + printTitlesRow = new SheetRangeImpl(sheet, 0, firstRow, + 255, lastRow); + } + + /** + * Sets the print column titles for this sheet + * + * @param firstRow the first row of the print titles + * @param lastRow the last row of the print titles + */ + public void setPrintTitlesCol(int firstCol, + int lastCol) + { + printTitlesCol = new SheetRangeImpl(sheet, firstCol, 0, + lastCol, 65535); + } + + /** + * Accessor for the print row titles + * + * @return the print row titles, or NULL if one is not defined for this sheet + */ + public Range getPrintTitlesRow() + { + return printTitlesRow; + } + + /** + * Accessor for the print column titles + * + * @return the print column titles, or NULL if one is not defined for this + * sheet + */ + public Range getPrintTitlesCol() + { + return printTitlesCol; + } +} Index: 3rdParty_sources/jexcelapi/jxl/StringFormulaCell.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/StringFormulaCell.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/StringFormulaCell.java 17 Aug 2012 14:51:24 -0000 1.1 @@ -0,0 +1,30 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl; + +/** + * A mixin interface for numerical formulas, which combines the interfaces + * for formulas and for strings + */ +public interface StringFormulaCell extends LabelCell, FormulaCell +{ +} + + Index: 3rdParty_sources/jexcelapi/jxl/Workbook.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/Workbook.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/Workbook.java 17 Aug 2012 14:51:25 -0000 1.1 @@ -0,0 +1,419 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import jxl.read.biff.BiffException; +import jxl.read.biff.File; +import jxl.read.biff.PasswordException; +import jxl.read.biff.WorkbookParser; +import jxl.write.WritableWorkbook; +import jxl.write.biff.WritableWorkbookImpl; + +/** + * Represents a Workbook. Contains the various factory methods and provides + * a variety of accessors which provide access to the work sheets. + */ +public abstract class Workbook +{ + /** + * The current version of the software + */ + private static final String VERSION = "2.6.9"; + + /** + * The constructor + */ + protected Workbook() + { + } + + /** + * Gets the sheets within this workbook. Use of this method for + * large worksheets can cause performance problems. + * + * @return an array of the individual sheets + */ + public abstract Sheet[] getSheets(); + + /** + * Gets the sheet names + * + * @return an array of strings containing the sheet names + */ + public abstract String[] getSheetNames(); + + /** + * Gets the specified sheet within this workbook + * As described in the accompanying technical notes, each call + * to getSheet forces a reread of the sheet (for memory reasons). + * Therefore, do not make unnecessary calls to this method. Furthermore, + * do not hold unnecessary references to Sheets in client code, as + * this will prevent the garbage collector from freeing the memory + * + * @param index the zero based index of the reQuired sheet + * @return The sheet specified by the index + * @exception IndexOutOfBoundException when index refers to a non-existent + * sheet + */ + public abstract Sheet getSheet(int index) + throws IndexOutOfBoundsException; + + /** + * Gets the sheet with the specified name from within this workbook. + * As described in the accompanying technical notes, each call + * to getSheet forces a reread of the sheet (for memory reasons). + * Therefore, do not make unnecessary calls to this method. Furthermore, + * do not hold unnecessary references to Sheets in client code, as + * this will prevent the garbage collector from freeing the memory + * + * @param name the sheet name + * @return The sheet with the specified name, or null if it is not found + */ + public abstract Sheet getSheet(String name); + + /** + * Accessor for the software version + * + * @return the version + */ + public static String getVersion() + { + return VERSION; + } + + /** + * Returns the number of sheets in this workbook + * + * @return the number of sheets in this workbook + */ + public abstract int getNumberOfSheets(); + + /** + * Gets the named cell from this workbook. If the name refers to a + * range of cells, then the cell on the top left is returned. If + * the name cannot be found, null is returned. + * This is a convenience function to quickly access the contents + * of a single cell. If you need further information (such as the + * sheet or adjacent cells in the range) use the functionally + * richer method, findByName which returns a list of ranges + * + * @param name the name of the cell/range to search for + * @return the cell in the top left of the range if found, NULL + * otherwise + */ + public abstract Cell findCellByName(String name); + + /** + * Returns the cell for the specified location eg. "Sheet1!A4". + * This is identical to using the CellReferenceHelper with its + * associated performance overheads, consequently it should + * be use sparingly + * + * @param loc the cell to retrieve + * @return the cell at the specified location + */ + public abstract Cell getCell(String loc); + + /** + * Gets the named range from this workbook. The Range object returns + * contains all the cells from the top left to the bottom right + * of the range. + * If the named range comprises an adjacent range, + * the Range[] will contain one object; for non-adjacent + * ranges, it is necessary to return an array of length greater than + * one. + * If the named range contains a single cell, the top left and + * bottom right cell will be the same cell + * + * @param name the name of the cell/range to search for + * @return the range of cells, or NULL if the range does not exist + */ + public abstract Range[] findByName(String name); + + /** + * Gets the named ranges + * + * @return the list of named cells within the workbook + */ + public abstract String[] getRangeNames(); + + + /** + * Determines whether the sheet is protected + * + * @return TRUE if the workbook is protected, FALSE otherwise + */ + public abstract boolean isProtected(); + + /** + * Parses the excel file. + * If the workbook is password protected a PasswordException is thrown + * in case consumers of the API wish to handle this in a particular way + * + * @exception BiffException + * @exception PasswordException + */ + protected abstract void parse() throws BiffException, PasswordException; + + /** + * Closes this workbook, and frees makes any memory allocated available + * for garbage collection + */ + public abstract void close(); + + /** + * A factory method which takes in an excel file and reads in the contents. + * + * @exception IOException + * @exception BiffException + * @param file the excel 97 spreadsheet to parse + * @return a workbook instance + */ + public static Workbook getWorkbook(java.io.File file) + throws IOException, BiffException + { + return getWorkbook(file, new WorkbookSettings()); + } + + /** + * A factory method which takes in an excel file and reads in the contents. + * + * @exception IOException + * @exception BiffException + * @param file the excel 97 spreadsheet to parse + * @param ws the settings for the workbook + * @return a workbook instance + */ + public static Workbook getWorkbook(java.io.File file, WorkbookSettings ws) + throws IOException, BiffException + { + FileInputStream fis = new FileInputStream(file); + + // Always close down the input stream, regardless of whether or not the + // file can be parsed. Thanks to Steve Hahn for this + File dataFile = null; + + try + { + dataFile = new File(fis, ws); + } + catch (IOException e) + { + fis.close(); + throw e; + } + catch (BiffException e) + { + fis.close(); + throw e; + } + + fis.close(); + + Workbook workbook = new WorkbookParser(dataFile, ws); + workbook.parse(); + + return workbook; + } + + /** + * A factory method which takes in an excel file and reads in the contents. + * + * @param is an open stream which is the the excel 97 spreadsheet to parse + * @return a workbook instance + * @exception IOException + * @exception BiffException + */ + public static Workbook getWorkbook(InputStream is) + throws IOException, BiffException + { + return getWorkbook(is, new WorkbookSettings()); + } + + /** + * A factory method which takes in an excel file and reads in the contents. + * + * @param is an open stream which is the the excel 97 spreadsheet to parse + * @param ws the settings for the workbook + * @return a workbook instance + * @exception IOException + * @exception BiffException + */ + public static Workbook getWorkbook(InputStream is, WorkbookSettings ws) + throws IOException, BiffException + { + File dataFile = new File(is, ws); + + Workbook workbook = new WorkbookParser(dataFile, ws); + workbook.parse(); + + return workbook; + } + + /** + * Creates a writable workbook with the given file name + * + * @param file the workbook to copy + * @return a writable workbook + * @exception IOException + */ + public static WritableWorkbook createWorkbook(java.io.File file) + throws IOException + { + return createWorkbook(file, new WorkbookSettings()); + } + + /** + * Creates a writable workbook with the given file name + * + * @param file the file to copy from + * @param ws the global workbook settings + * @return a writable workbook + * @exception IOException + */ + public static WritableWorkbook createWorkbook(java.io.File file, + WorkbookSettings ws) + throws IOException + { + FileOutputStream fos = new FileOutputStream(file); + WritableWorkbook w = new WritableWorkbookImpl(fos, true, ws); + return w; + } + + /** + * Creates a writable workbook with the given filename as a copy of + * the workbook passed in. Once created, the contents of the writable + * workbook may be modified + * + * @param file the output file for the copy + * @param in the workbook to copy + * @return a writable workbook + * @exception IOException + */ + public static WritableWorkbook createWorkbook(java.io.File file, + Workbook in) + throws IOException + { + return createWorkbook(file, in, new WorkbookSettings()); + } + + /** + * Creates a writable workbook with the given filename as a copy of + * the workbook passed in. Once created, the contents of the writable + * workbook may be modified + * + * @param file the output file for the copy + * @param in the workbook to copy + * @param ws the configuration for this workbook + * @return a writable workbook + */ + public static WritableWorkbook createWorkbook(java.io.File file, + Workbook in, + WorkbookSettings ws) + throws IOException + { + FileOutputStream fos = new FileOutputStream(file); + WritableWorkbook w = new WritableWorkbookImpl(fos, in, true, ws); + return w; + } + + /** + * Creates a writable workbook as a copy of + * the workbook passed in. Once created, the contents of the writable + * workbook may be modified + * + * @param os the stream to write to + * @param in the workbook to copy + * @return a writable workbook + * @exception IOException + */ + public static WritableWorkbook createWorkbook(OutputStream os, + Workbook in) + throws IOException + { + return createWorkbook(os, in, ((WorkbookParser) in).getSettings()); + } + + /** + * Creates a writable workbook as a copy of + * the workbook passed in. Once created, the contents of the writable + * workbook may be modified + * + * @param os the output stream to write to + * @param in the workbook to copy + * @param ws the configuration for this workbook + * @return a writable workbook + * @exception IOException + */ + public static WritableWorkbook createWorkbook(OutputStream os, + Workbook in, + WorkbookSettings ws) + throws IOException + { + WritableWorkbook w = new WritableWorkbookImpl(os, in, false, ws); + return w; + } + + /** + * Creates a writable workbook. When the workbook is closed, + * it will be streamed directly to the output stream. In this + * manner, a generated excel spreadsheet can be passed from + * a servlet to the browser over HTTP + * + * @param os the output stream + * @return the writable workbook + * @exception IOException + */ + public static WritableWorkbook createWorkbook(OutputStream os) + throws IOException + { + return createWorkbook(os, new WorkbookSettings()); + } + + /** + * Creates a writable workbook. When the workbook is closed, + * it will be streamed directly to the output stream. In this + * manner, a generated excel spreadsheet can be passed from + * a servlet to the browser over HTTP + * + * @param os the output stream + * @param ws the configuration for this workbook + * @return the writable workbook + * @exception IOException + */ + public static WritableWorkbook createWorkbook(OutputStream os, + WorkbookSettings ws) + throws IOException + { + WritableWorkbook w = new WritableWorkbookImpl(os, false, ws); + return w; + } +} + + + + + Index: 3rdParty_sources/jexcelapi/jxl/WorkbookSettings.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/WorkbookSettings.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/WorkbookSettings.java 17 Aug 2012 14:51:24 -0000 1.1 @@ -0,0 +1,715 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl; + +import java.io.File; +import java.util.HashMap; +import java.util.Locale; + +import common.Logger; + +import jxl.biff.CountryCode; +import jxl.biff.formula.FunctionNames; + +/** + * This is a bean which client applications may use to set various advanced + * workbook properties. Use of this bean is not mandatory, and its absence + * will merely result in workbooks being read/written using the default + * settings + */ +public final class WorkbookSettings +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(WorkbookSettings.class); + + /** + * The amount of memory allocated to store the workbook data when + * reading a worksheet. For processeses reading many small workbooks inside + * a WAS it might be necessary to reduce the default size + */ + private int initialFileSize; + + /** + * The amount of memory allocated to the array containing the workbook + * data when its current amount is exhausted. + */ + private int arrayGrowSize; + + /** + * Flag to indicate whether the drawing feature is enabled or not + * Drawings deactivated using -Djxl.nodrawings=true on the JVM command line + * Activated by default or by using -Djxl.nodrawings=false on the JVM command + * line + */ + private boolean drawingsDisabled; + + /** + * Flag to indicate whether the name feature is enabled or not + * Names deactivated using -Djxl.nonames=true on the JVM command line + * Activated by default or by using -Djxl.nonames=false on the JVM command + * line + */ + private boolean namesDisabled; + + /** + * Flag to indicate whether formula cell references should be adjusted + * following row/column insertion/deletion + */ + private boolean formulaReferenceAdjustDisabled; + + /** + * Flag to indicate whether the system hint garbage collection + * is enabled or not. + * As a rule of thumb, it is desirable to enable garbage collection + * when reading large spreadsheets from a batch process or from the + * command line, but better to deactivate the feature when reading + * large spreadsheets within a WAS, as the calls to System.gc() not + * only garbage collect the junk in JExcelApi, but also in the + * webservers JVM and can cause significant slowdown + * GC deactivated using -Djxl.nogc=true on the JVM command line + * Activated by default or by using -Djxl.nogc=false on the JVM command line + */ + private boolean gcDisabled; + + /** + * Flag to indicate whether the rationalization of cell formats is + * disabled or not. + * Rationalization is enabled by default, but may be disabled for + * performance reasons. It can be deactivated using -Djxl.norat=true on + * the JVM command line + */ + private boolean rationalizationDisabled; + + /** + * Flag to indicate whether or not the merged cell checking has been + * disabled + */ + private boolean mergedCellCheckingDisabled; + + /** + * Flag to indicate whether the copying of additional property sets + * are disabled + */ + private boolean propertySetsDisabled; + + /** + * Flag to indicate that cell validation criteria are ignored + */ + private boolean cellValidationDisabled; + + /** + * Flag to indicate whether or not to ignore blank cells when processing + * sheets. Cells which are identified as blank can still have associated + * cell formats which the processing program may still need to read + */ + private boolean ignoreBlankCells; + + /** + * Flag to indicate whether auto filtering should be read/copied + */ + private boolean autoFilterDisabled; + + /** + * Flag to indicate whether a temporary file should be used when + * writing out the workbook + */ + private boolean useTemporaryFileDuringWrite; + + /** + * The directory for used for the temporary file during write. If this + * is NULL, the default system directory is used + */ + private File temporaryFileDuringWriteDirectory; + + /** + * The locale. Normally this is the same as the system locale, but there + * may be cases (eg. where you are uploading many spreadsheets from foreign + * sources) where you may want to specify the locale on an individual + * worksheet basis + * The locale may also be specified on the command line using the lang and + * country System properties eg. -Djxl.lang=en -Djxl.country=UK for UK + * English + */ + private Locale locale; + + /** + * The locale specific function names for this workbook + */ + private FunctionNames functionNames; + + /** + * The character encoding used for reading non-unicode strings. This can + * be different from the default platform encoding if processing spreadsheets + * from abroad. This may also be set using the system property jxl.encoding + */ + private String encoding; + + /** + * The character set used by the readable spreadsheeet + */ + private int characterSet; + + /** + * The display language used by Excel (ISO 3166 mnemonic) + */ + private String excelDisplayLanguage; + + /** + * The regional settings used by Excel (ISO 3166 mnemonic) + */ + private String excelRegionalSettings; + + /** + * A hash map of function names keyed on locale + */ + private HashMap localeFunctionNames; + + // ** + // The default values + // ** + private static final int DEFAULT_INITIAL_FILE_SIZE = 5 * 1024 * 1024; + // 5 megabytes + private static final int DEFAULT_ARRAY_GROW_SIZE = 1024 * 1024; // 1 megabyte + + /** + * Default constructor + */ + public WorkbookSettings() + { + initialFileSize = DEFAULT_INITIAL_FILE_SIZE; + arrayGrowSize = DEFAULT_ARRAY_GROW_SIZE; + localeFunctionNames = new HashMap(); + excelDisplayLanguage = CountryCode.USA.getCode(); + excelRegionalSettings = CountryCode.UK.getCode(); + + // Initialize other properties from the system properties + try + { + boolean suppressWarnings = Boolean.getBoolean("jxl.nowarnings"); + setSuppressWarnings(suppressWarnings); + drawingsDisabled = Boolean.getBoolean("jxl.nodrawings"); + namesDisabled = Boolean.getBoolean("jxl.nonames"); + gcDisabled = Boolean.getBoolean("jxl.nogc"); + rationalizationDisabled = Boolean.getBoolean("jxl.norat"); + mergedCellCheckingDisabled = + Boolean.getBoolean("jxl.nomergedcellchecks"); + formulaReferenceAdjustDisabled = + Boolean.getBoolean("jxl.noformulaadjust"); + propertySetsDisabled = Boolean.getBoolean("jxl.nopropertysets"); + ignoreBlankCells = Boolean.getBoolean("jxl.ignoreblanks"); + cellValidationDisabled = Boolean.getBoolean("jxl.nocellvalidation"); + autoFilterDisabled = !Boolean.getBoolean("jxl.autofilter"); + // autofilter currently disabled by default + useTemporaryFileDuringWrite = + Boolean.getBoolean("jxl.usetemporaryfileduringwrite"); + String tempdir = + System.getProperty("jxl.temporaryfileduringwritedirectory"); + + if (tempdir != null) + { + temporaryFileDuringWriteDirectory = new File(tempdir); + } + + encoding = System.getProperty("file.encoding"); + } + catch (SecurityException e) + { + logger.warn("Error accessing system properties.", e); + } + + // Initialize the locale to the system locale + try + { + if (System.getProperty("jxl.lang") == null || + System.getProperty("jxl.country") == null) + { + locale = Locale.getDefault(); + } + else + { + locale = new Locale(System.getProperty("jxl.lang"), + System.getProperty("jxl.country")); + } + + if (System.getProperty("jxl.encoding") != null) + { + encoding = System.getProperty("jxl.encoding"); + } + } + catch (SecurityException e) + { + logger.warn("Error accessing system properties.", e); + locale = Locale.getDefault(); + } + } + + /** + * Sets the amount of memory by which to increase the amount of + * memory allocated to storing the workbook data. + * For processeses reading many small workbooks + * inside a WAS it might be necessary to reduce the default size + * Default value is 1 megabyte + * + * @param sz the file size in bytes + */ + public void setArrayGrowSize(int sz) + { + arrayGrowSize = sz; + } + + /** + * Accessor for the array grow size property + * + * @return the array grow size + */ + public int getArrayGrowSize() + { + return arrayGrowSize; + } + + /** + * Sets the initial amount of memory allocated to store the workbook data + * when reading a worksheet. For processeses reading many small workbooks + * inside a WAS it might be necessary to reduce the default size + * Default value is 5 megabytes + * + * @param sz the file size in bytes + */ + public void setInitialFileSize(int sz) + { + initialFileSize = sz; + } + + /** + * Accessor for the initial file size property + * + * @return the initial file size + */ + public int getInitialFileSize() + { + return initialFileSize; + } + + /** + * Gets the drawings disabled flag + * + * @return TRUE if drawings are disabled, FALSE otherwise + */ + public boolean getDrawingsDisabled() + { + return drawingsDisabled; + } + + /** + * Accessor for the disabling of garbage collection + * + * @return FALSE if JExcelApi hints for garbage collection, TRUE otherwise + */ + public boolean getGCDisabled() + { + return gcDisabled; + } + + /** + * Accessor for the disabling of interpretation of named ranges + * + * @return FALSE if named cells are interpreted, TRUE otherwise + */ + public boolean getNamesDisabled() + { + return namesDisabled; + } + + /** + * Disables the handling of names + * + * @param b TRUE to disable the names feature, FALSE otherwise + */ + public void setNamesDisabled(boolean b) + { + namesDisabled = b; + } + + /** + * Disables the handling of drawings + * + * @param b TRUE to disable the names feature, FALSE otherwise + */ + public void setDrawingsDisabled(boolean b) + { + drawingsDisabled = b; + } + + /** + * Sets whether or not to rationalize the cell formats before + * writing out the sheet. The default value is true + * + * @param r the rationalization flag + */ + public void setRationalization(boolean r) + { + rationalizationDisabled = !r; + } + + /** + * Accessor to retrieve the rationalization flag + * + * @return TRUE if rationalization is off, FALSE if rationalization is on + */ + public boolean getRationalizationDisabled() + { + return rationalizationDisabled; + } + + /** + * Accessor to retrieve the merged cell checking flag + * + * @return TRUE if merged cell checking is off, FALSE if it is on + */ + public boolean getMergedCellCheckingDisabled() + { + return mergedCellCheckingDisabled; + } + + /** + * Accessor to set the merged cell checking + * + * @param b - TRUE to enable merged cell checking, FALSE otherwise + */ + public void setMergedCellChecking(boolean b) + { + mergedCellCheckingDisabled = !b; + } + + /** + * Sets whether or not to enable any property sets (such as macros) + * to be copied along with the workbook + * Leaving this feature enabled will result in the JXL process using + * more memory + * + * @param r the property sets flag + */ + public void setPropertySets(boolean r) + { + propertySetsDisabled = !r; + } + + /** + * Accessor to retrieve the property sets disabled flag + * + * @return TRUE if property sets are disabled, FALSE otherwise + */ + public boolean getPropertySetsDisabled() + { + return propertySetsDisabled; + } + + /** + * Accessor to set the suppress warnings flag. Due to the change + * in logging in version 2.4, this will now set the warning + * behaviour across the JVM (depending on the type of logger used) + * + * @param w the flag + */ + public void setSuppressWarnings(boolean w) + { + logger.setSuppressWarnings(w); + } + + /** + * Accessor for the formula adjust disabled + * + * @return TRUE if formulas are adjusted following row/column inserts/deletes + * FALSE otherwise + */ + public boolean getFormulaAdjust() + { + return !formulaReferenceAdjustDisabled; + } + + /** + * Setter for the formula adjust disabled property + * + * @param b TRUE to adjust formulas, FALSE otherwise + */ + public void setFormulaAdjust(boolean b) + { + formulaReferenceAdjustDisabled = !b; + } + + /** + * Sets the locale used by JExcelApi to generate the spreadsheet. + * Setting this value has no effect on the language or region of + * the generated excel file + * + * @param l the locale + */ + public void setLocale(Locale l) + { + locale = l; + } + + /** + * Returns the locale used by JExcelAPI to read the spreadsheet + * + * @return the locale + */ + public Locale getLocale() + { + return locale; + } + + /** + * Accessor for the character encoding + * + * @return the character encoding for this workbook + */ + public String getEncoding() + { + return encoding; + } + + /** + * Sets the encoding for this workbook + * + * @param enc the encoding + */ + public void setEncoding(String enc) + { + encoding = enc; + } + + /** + * Gets the function names. This is used by the formula parsing package + * in order to get the locale specific function names for this particular + * workbook + * + * @return the list of function names + */ + public FunctionNames getFunctionNames() + { + if (functionNames == null) + { + functionNames = (FunctionNames) localeFunctionNames.get(locale); + + // have not previously accessed function names for this locale, + // so create a brand new one and add it to the list + if (functionNames == null) + { + functionNames = new FunctionNames(locale); + localeFunctionNames.put(locale, functionNames); + } + } + + return functionNames; + } + + /** + * Accessor for the character set. This value is only used for reading + * and has no effect when writing out the spreadsheet + * + * @return the character set used by this spreadsheet + */ + public int getCharacterSet() + { + return characterSet; + } + + /** + * Sets the character set. This is only used when the spreadsheet is + * read, and has no effect when the spreadsheet is written + * + * @param cs the character set encoding value + */ + public void setCharacterSet(int cs) + { + characterSet = cs; + } + + /** + * Sets the garbage collection disabled + * + * @param disabled TRUE to disable garbage collection, FALSE to enable it + */ + public void setGCDisabled(boolean disabled) + { + gcDisabled = disabled; + } + + /** + * Sets the ignore blanks flag + * + * @param ignoreBlanks TRUE to ignore blanks, FALSE to take them into account + */ + public void setIgnoreBlanks(boolean ignoreBlanks) + { + ignoreBlankCells = ignoreBlanks; + } + + /** + * Accessor for the ignore blanks flag + * + * @return TRUE if blank cells are being ignored, FALSE otherwise + */ + public boolean getIgnoreBlanks() + { + return ignoreBlankCells; + } + + /** + * Sets the ignore cell validation flag + * + * @param cv TRUE to disable cell validation, FALSE to enable it + */ + public void setCellValidationDisabled(boolean cv) + { + cellValidationDisabled = cv; + } + + /** + * Accessor for the ignore cell validation + * + * @return TRUE if cell validation is disabled + */ + public boolean getCellValidationDisabled() + { + return cellValidationDisabled; + } + + /** + * Returns the two character ISO 3166 mnemonic used by excel for user + * language displayto display + * @return the display language + */ + public String getExcelDisplayLanguage() + { + return excelDisplayLanguage; + } + + /** + * Returns the two character ISO 3166 mnemonic used by excel for + * its regional settings + * @return the regional settings + */ + public String getExcelRegionalSettings() + { + return excelRegionalSettings; + } + + /** + * Sets the language in which the generated file will display + * + * @param code the two character ISO 3166 country code + */ + public void setExcelDisplayLanguage(String code) + { + excelDisplayLanguage = code; + } + + /** + * Sets the regional settings for the generated excel file + * + * @param code the two character ISO 3166 country code + */ + public void setExcelRegionalSettings(String code) + { + excelRegionalSettings = code; + } + + /** + * Accessor for the autofilter disabled feature + * + * @return TRUE if autofilter is disabled, FALSE otherwise + */ + public boolean getAutoFilterDisabled() + { + return autoFilterDisabled; + } + + /** + * Sets the autofilter disabled + * + * @param disabled + */ + public void setAutoFilterDisabled(boolean disabled) + { + autoFilterDisabled = disabled; + } + + /** + * Accessor for the temporary file during write. If this is set, then + * when the workbook is written a temporary file will be used to store + * the interim binary data, otherwise it will take place in memory. Setting + * this flag involves an assessment of the trade-offs between memory usage + * and performance + * + * @return TRUE if a temporary is file is used during writing, + * FALSE otherwise + */ + public boolean getUseTemporaryFileDuringWrite() + { + return useTemporaryFileDuringWrite; + } + + /** + * Sets whether a temporary file is used during the generation of + * the workbook. If not set, the workbook will take place entirely in + * memory. Setting + * this flag involves an assessment of the trade-offs between memory usage + * and performance + * + * @return TRUE if a temporary is file is used during writing, + * FALSE otherwise + */ + public void setUseTemporaryFileDuringWrite(boolean temp) + { + useTemporaryFileDuringWrite = temp; + } + + /** + * Used in conjunction with the UseTemporaryFileDuringWrite setting to + * set the target directory for the temporary files. If this is not set, + * the system default temporary directory is used. + * This has no effect unless the useTemporaryFileDuringWrite setting + * is TRUE + * + * @param dir the directory to which temporary files should be written + */ + public void setTemporaryFileDuringWriteDirectory(File dir) + { + temporaryFileDuringWriteDirectory = dir; + } + + /** + * Used in conjunction with the UseTemporaryFileDuringWrite setting to + * set the target directory for the temporary files. This value can + * be NULL, in which case the normal system default temporary directory + * is used instead + * + * @return the temporary directory used during write, or NULL if it is + * not set + */ + public File getTemporaryFileDuringWriteDirectory() + { + return temporaryFileDuringWriteDirectory; + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/AutoFilter.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/AutoFilter.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/AutoFilter.java 17 Aug 2012 14:51:14 -0000 1.1 @@ -0,0 +1,73 @@ +/********************************************************************* +* +* Copyright (C) 2007 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +import java.io.IOException; + +import jxl.write.biff.File; + +/** + * Information for autofiltering + */ +public class AutoFilter +{ + private FilterModeRecord filterMode; + private AutoFilterInfoRecord autoFilterInfo; + private AutoFilterRecord autoFilter; + + /** + * Constructor + */ + public AutoFilter(FilterModeRecord fmr, + AutoFilterInfoRecord afir) + { + filterMode = fmr; + autoFilterInfo = afir; + } + + public void add(AutoFilterRecord af) + { + autoFilter = af; // make this into a list sometime + } + + /** + * Writes out the data validation + * + * @exception IOException + * @param outputFile the output file + */ + public void write(File outputFile) throws IOException + { + if (filterMode != null) + { + outputFile.write(filterMode); + } + + if (autoFilterInfo != null) + { + outputFile.write(autoFilterInfo); + } + + if (autoFilter != null) + { + outputFile.write(autoFilter); + } + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/AutoFilterInfoRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/AutoFilterInfoRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/AutoFilterInfoRecord.java 17 Aug 2012 14:51:14 -0000 1.1 @@ -0,0 +1,60 @@ +/********************************************************************* +* +* Copyright (C) 2007 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +import common.Logger; + +import jxl.read.biff.Record; + +/** + * Range information for conditional formatting + */ +public class AutoFilterInfoRecord extends WritableRecordData +{ + // The logger + private static Logger logger = Logger.getLogger(AutoFilterInfoRecord.class); + + /** + * The data + */ + private byte[] data; + + + /** + * Constructor + */ + public AutoFilterInfoRecord(Record t) + { + super(t); + + data = getRecord().getData(); + } + + /** + * Retrieves the data for output to binary file + * + * @return the data to be written + */ + public byte[] getData() + { + return data; + } +} + Index: 3rdParty_sources/jexcelapi/jxl/biff/AutoFilterRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/AutoFilterRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/AutoFilterRecord.java 17 Aug 2012 14:51:13 -0000 1.1 @@ -0,0 +1,60 @@ +/********************************************************************* +* +* Copyright (C) 2007 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +import common.Logger; + +import jxl.read.biff.Record; + +/** + * Range information for conditional formatting + */ +public class AutoFilterRecord extends WritableRecordData +{ + // The logger + private static Logger logger = Logger.getLogger(AutoFilterRecord.class); + + /** + * The data + */ + private byte[] data; + + + /** + * Constructor + */ + public AutoFilterRecord(Record t) + { + super(t); + + data = getRecord().getData(); + } + + /** + * Retrieves the data for output to binary file + * + * @return the data to be written + */ + public byte[] getData() + { + return data; + } +} + Index: 3rdParty_sources/jexcelapi/jxl/biff/BaseCellFeatures.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/BaseCellFeatures.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/BaseCellFeatures.java 17 Aug 2012 14:51:13 -0000 1.1 @@ -0,0 +1,434 @@ +/********************************************************************* +* +* Copyright (C) 2004 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +import java.util.Collection; + +import common.Assert; +import common.Logger; + +import jxl.biff.drawing.ComboBox; +import jxl.biff.drawing.Comment; +import jxl.write.biff.CellValue; + +/** + * Container for any additional cell features + */ +public class BaseCellFeatures +{ + /** + * The logger + */ + public static Logger logger = Logger.getLogger(BaseCellFeatures.class); + + /** + * The comment + */ + private String comment; + + /** + * The comment width in cells + */ + private double commentWidth; + + /** + * The comment height in cells + */ + private double commentHeight; + + /** + * A handle to the drawing object + */ + private Comment commentDrawing; + + /** + * A handle to the combo box object + */ + private ComboBox comboBox; + + /** + * The data validation settings + */ + private DataValiditySettingsRecord validationSettings; + + /** + * The DV Parser used to contain the validation details + */ + private DVParser dvParser; + + /** + * Indicates whether a drop down is required + */ + private boolean dropDown; + + /** + * Indicates whether this cell features has data validation + */ + private boolean dataValidation; + + /** + * The cell to which this is attached, and which may need to be notified + */ + private CellValue writableCell; + + // Constants + private final static double defaultCommentWidth = 3; + private final static double defaultCommentHeight = 4; + + // Validation conditions + protected static class ValidationCondition + { + private DVParser.Condition condition; + + private static ValidationCondition[] types = new ValidationCondition[0]; + + ValidationCondition(DVParser.Condition c) + { + condition = c; + ValidationCondition[] oldtypes = types; + types = new ValidationCondition[oldtypes.length+1]; + System.arraycopy(oldtypes, 0, types, 0, oldtypes.length); + types[oldtypes.length] = this; + } + + public DVParser.Condition getCondition() + { + return condition; + } + } + + public static final ValidationCondition BETWEEN = + new ValidationCondition(DVParser.BETWEEN); + public static final ValidationCondition NOT_BETWEEN = + new ValidationCondition(DVParser.NOT_BETWEEN); + public static final ValidationCondition EQUAL = + new ValidationCondition(DVParser.EQUAL); + public static final ValidationCondition NOT_EQUAL = + new ValidationCondition(DVParser.NOT_EQUAL); + public static final ValidationCondition GREATER_THAN = + new ValidationCondition(DVParser.GREATER_THAN); + public static final ValidationCondition LESS_THAN = + new ValidationCondition(DVParser.LESS_THAN); + public static final ValidationCondition GREATER_EQUAL = + new ValidationCondition(DVParser.GREATER_EQUAL); + public static final ValidationCondition LESS_EQUAL = + new ValidationCondition(DVParser.LESS_EQUAL); + + /** + * Constructor + */ + protected BaseCellFeatures() + { + } + + /** + * Copy constructor + * + * @param the cell to copy + */ + public BaseCellFeatures(BaseCellFeatures cf) + { + // The comment stuff + comment = cf.comment; + commentWidth = cf.commentWidth; + commentHeight = cf.commentHeight; + + // The data validation stuff. + dropDown = cf.dropDown; + dataValidation = cf.dataValidation; + + validationSettings = cf.validationSettings; // ? + + if (cf.dvParser != null) + { + dvParser = new DVParser(cf.dvParser); + } + } + + /** + * Accessor for the cell comment + */ + protected String getComment() + { + return comment; + } + + /** + * Accessor for the comment width + */ + public double getCommentWidth() + { + return commentWidth; + } + + /** + * Accessor for the comment height + */ + public double getCommentHeight() + { + return commentHeight; + } + + /** + * Called by the cell when the features are added + * + * @param wc the writable cell + */ + public final void setWritableCell(CellValue wc) + { + writableCell = wc; + } + + /** + * Internal method to set the cell comment. Used when reading + */ + public void setReadComment(String s, double w, double h) + { + comment = s; + commentWidth = w; + commentHeight = h; + } + + /** + * Internal method to set the data validation. Used when reading + */ + public void setValidationSettings(DataValiditySettingsRecord dvsr) + { + Assert.verify(dvsr != null); + + validationSettings = dvsr; + dataValidation = true; + } + + /** + * Sets the cell comment + * + * @param s the comment + */ + public void setComment(String s) + { + setComment(s, defaultCommentWidth, defaultCommentHeight); + } + + /** + * Sets the cell comment + * + * @param s the comment + * @param height the height of the comment box in cells + * @param width the width of the comment box in cells + */ + public void setComment(String s, double width, double height) + { + comment = s; + commentWidth = width; + commentHeight = height; + + if (commentDrawing != null) + { + commentDrawing.setCommentText(s); + commentDrawing.setWidth(width); + commentDrawing.setWidth(height); + // commentDrawing is set up when trying to modify a copied cell + } + } + + /** + * Removes the cell comment, if present + */ + public void removeComment() + { + // Set the comment string to be empty + comment = null; + + // Remove the drawing from the drawing group + if (commentDrawing != null) + { + // do not call DrawingGroup.remove() because comments are not present + // on the Workbook DrawingGroup record + writableCell.removeComment(commentDrawing); + commentDrawing = null; + } + } + + /** + * Removes any data validation, if present + */ + public void removeDataValidation() + { + // Remove the validation from the WritableSheet object if present + if (dataValidation) + { + writableCell.removeDataValidation(); + } + + clearValidationSettings(); + } + + /** + * Sets the comment drawing object + */ + public final void setCommentDrawing(Comment c) + { + commentDrawing = c; + } + + /** + * Accessor for the comment drawing + */ + public final Comment getCommentDrawing() + { + return commentDrawing; + } + + /** + * Gets the data validation list as a formula. Used only when reading + * + * @return the validation formula as a list + */ + public String getDataValidationList() + { + if (validationSettings == null) + { + return null; + } + + return validationSettings.getValidationFormula(); + } + + /** + * The list of items to validate for this cell. For each object in the + * collection, the toString() method will be called and the data entered + * will be validated against that string + * + * @param c the list of valid values + */ + public void setDataValidationList(Collection c) + { + clearValidationSettings(); + dvParser = new DVParser(c); + dropDown = true; + dataValidation = true; + } + + /** + * The list of items to validate for this cell in the form of a cell range. + * + * @param c the list of valid values + */ + public void setDataValidationRange(int col1, int r1, int col2, int r2) + { + clearValidationSettings(); + dvParser = new DVParser(col1, r1, col2, r2); + dropDown = true; + dataValidation = true; + } + + /** + * Sets the data validation based upon a named range + */ + public void setDataValidationRange(String namedRange) + { + clearValidationSettings(); + dvParser = new DVParser(namedRange); + dropDown = true; + dataValidation = true; + } + + public void setNumberValidation(double val, ValidationCondition c) + { + clearValidationSettings(); + dvParser = new DVParser(val, Double.NaN, c.getCondition()); + dropDown = false; + dataValidation = true; + } + + public void setNumberValidation(double val1, double val2, + ValidationCondition c) + { + clearValidationSettings(); + dvParser = new DVParser(val1, val2, c.getCondition()); + dropDown = false; + dataValidation = true; + } + + /** + * Accessor for the data validation + * + * @return TRUE if this has a data validation associated with it, + FALSE otherwise + */ + public boolean hasDataValidation() + { + return dataValidation; + } + + /** + * Clears out any existing validation settings + */ + private void clearValidationSettings() + { + validationSettings = null; + dvParser = null; + dropDown = false; + comboBox = null; + dataValidation = false; + } + + /** + * Accessor for whether a drop down is required + * + * @return TRUE if this requires a drop down, FALSE otherwise + */ + public boolean hasDropDown() + { + return dropDown; + } + + /** + * Sets the combo box drawing object for list validations + * + * @param cb the combo box + */ + public void setComboBox(ComboBox cb) + { + comboBox = cb; + } + + /** + * Gets the dv parser + */ + public DVParser getDVParser() + { + // straightforward - this was created as a writable cell + if (dvParser != null) + { + return dvParser; + } + + // this was copied from a readable cell, and then copied again + if (validationSettings != null) + { + dvParser = new DVParser(validationSettings.getDVParser()); + return dvParser; + } + + return null; // keep the compiler happy + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/BaseCompoundFile.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/BaseCompoundFile.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/BaseCompoundFile.java 17 Aug 2012 14:51:13 -0000 1.1 @@ -0,0 +1,349 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +import common.Assert; +import common.Logger; + +/** + * Contains the common data for a compound file + */ +public abstract class BaseCompoundFile +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(BaseCompoundFile.class); + + /** + * The identifier at the beginning of every OLE file + */ + protected static final byte[] IDENTIFIER = new byte[] + {(byte) 0xd0, + (byte) 0xcf, + (byte) 0x11, + (byte) 0xe0, + (byte) 0xa1, + (byte) 0xb1, + (byte) 0x1a, + (byte) 0xe1}; + /** + */ + protected static final int NUM_BIG_BLOCK_DEPOT_BLOCKS_POS = 0x2c; + /** + */ + protected static final int SMALL_BLOCK_DEPOT_BLOCK_POS = 0x3c; + /** + */ + protected static final int NUM_SMALL_BLOCK_DEPOT_BLOCKS_POS = 0x40; + /** + */ + protected static final int ROOT_START_BLOCK_POS = 0x30; + /** + */ + protected static final int BIG_BLOCK_SIZE = 0x200; + /** + */ + protected static final int SMALL_BLOCK_SIZE = 0x40; + /** + */ + protected static final int EXTENSION_BLOCK_POS = 0x44; + /** + */ + protected static final int NUM_EXTENSION_BLOCK_POS = 0x48; + /** + */ + protected static final int PROPERTY_STORAGE_BLOCK_SIZE = 0x80; + /** + */ + protected static final int BIG_BLOCK_DEPOT_BLOCKS_POS = 0x4c; + /** + */ + protected static final int SMALL_BLOCK_THRESHOLD = 0x1000; + + // property storage offsets + /** + */ + private static final int SIZE_OF_NAME_POS = 0x40; + /** + */ + private static final int TYPE_POS = 0x42; + /** + */ + private static final int COLOUR_POS = 0x43; + /** + */ + private static final int PREVIOUS_POS = 0x44; + /** + */ + private static final int NEXT_POS = 0x48; + /** + */ + private static final int CHILD_POS = 0x4c; + /** + */ + private static final int START_BLOCK_POS = 0x74; + /** + */ + private static final int SIZE_POS = 0x78; + + /** + * The standard property sets + */ + public final static String ROOT_ENTRY_NAME = "Root Entry"; + public final static String WORKBOOK_NAME = "Workbook"; + public final static String SUMMARY_INFORMATION_NAME = + "\u0005SummaryInformation"; + public final static String DOCUMENT_SUMMARY_INFORMATION_NAME = + "\u0005DocumentSummaryInformation"; + public final static String COMP_OBJ_NAME = + "\u0001CompObj"; + public final static String[] STANDARD_PROPERTY_SETS = + new String[] {ROOT_ENTRY_NAME, WORKBOOK_NAME, + SUMMARY_INFORMATION_NAME, + DOCUMENT_SUMMARY_INFORMATION_NAME}; + + /** + * Property storage types + */ + public final static int NONE_PS_TYPE = 0; + public final static int DIRECTORY_PS_TYPE = 1; + public final static int FILE_PS_TYPE = 2; + public final static int ROOT_ENTRY_PS_TYPE = 5; + + + /** + * Inner class to represent the property storage sets. Access is public + * to allow access from the PropertySetsReader demo utility + */ + public class PropertyStorage + { + /** + * The name of this property set + */ + public String name; + /** + * The type of the property set + */ + public int type; + /** + * The colour of the property set + */ + public int colour; + /** + * The block number in the stream which this property sets starts at + */ + public int startBlock; + /** + * The size, in bytes, of this property set + */ + public int size; + /** + * The previous property set + */ + public int previous; + /** + * The next property set + */ + public int next; + /** + * The child for this property set + */ + public int child; + + /** + * The data that created this set + */ + public byte[] data; + + /** + * Constructs a property set + * + * @param d the bytes + */ + public PropertyStorage(byte[] d) + { + data = d; + int nameSize = IntegerHelper.getInt(data[SIZE_OF_NAME_POS], + data[SIZE_OF_NAME_POS + 1]); + + if (nameSize > SIZE_OF_NAME_POS) + { + logger.warn("property set name exceeds max length - truncating"); + nameSize = SIZE_OF_NAME_POS; + + } + type = data[TYPE_POS]; + colour = data[COLOUR_POS]; + + startBlock = IntegerHelper.getInt + (data[START_BLOCK_POS], + data[START_BLOCK_POS + 1], + data[START_BLOCK_POS + 2], + data[START_BLOCK_POS + 3]); + size = IntegerHelper.getInt + (data[SIZE_POS], + data[SIZE_POS + 1], + data[SIZE_POS + 2], + data[SIZE_POS + 3]); + previous = IntegerHelper.getInt + (data[PREVIOUS_POS], + data[PREVIOUS_POS+1], + data[PREVIOUS_POS+2], + data[PREVIOUS_POS+3]); + next = IntegerHelper.getInt + (data[NEXT_POS], + data[NEXT_POS+1], + data[NEXT_POS+2], + data[NEXT_POS+3]); + child = IntegerHelper.getInt + (data[CHILD_POS], + data[CHILD_POS+1], + data[CHILD_POS+2], + data[CHILD_POS+3]); + + int chars = 0; + if (nameSize > 2) + { + chars = (nameSize - 1) / 2; + } + + StringBuffer n = new StringBuffer(""); + for (int i = 0; i < chars ; i++) + { + n.append( (char) data[i * 2]); + } + + name = n.toString(); + } + + /** + * Constructs an empty property set. Used when writing the file + * + * @param name the property storage name + */ + public PropertyStorage(String name) + { + data = new byte[PROPERTY_STORAGE_BLOCK_SIZE]; + + Assert.verify(name.length() < 32); + + IntegerHelper.getTwoBytes((name.length() + 1) * 2, + data, + SIZE_OF_NAME_POS); + // add one to the name length to allow for the null character at + // the end + for (int i = 0; i < name.length(); i++) + { + data[i * 2] = (byte) name.charAt(i); + } + } + + /** + * Sets the type + * + * @param t the type + */ + public void setType(int t) + { + type = t; + data[TYPE_POS] = (byte) t; + } + + /** + * Sets the number of the start block + * + * @param sb the number of the start block + */ + public void setStartBlock(int sb) + { + startBlock = sb; + IntegerHelper.getFourBytes(sb, data, START_BLOCK_POS); + } + + /** + * Sets the size of the file + * + * @param s the size + */ + public void setSize(int s) + { + size = s; + IntegerHelper.getFourBytes(s, data, SIZE_POS); + } + + /** + * Sets the previous block + * + * @param prev the previous block + */ + public void setPrevious(int prev) + { + previous = prev; + IntegerHelper.getFourBytes(prev, data, PREVIOUS_POS); + } + + /** + * Sets the next block + * + * @param nxt the next block + */ + public void setNext(int nxt) + { + next = nxt; + IntegerHelper.getFourBytes(next, data, NEXT_POS); + } + + /** + * Sets the child + * + * @param dir the child + */ + public void setChild(int dir) + { + child = dir; + IntegerHelper.getFourBytes(child, data, CHILD_POS); + } + + /** + * Sets the colour + * + * @param col colour + */ + public void setColour(int col) + { + colour = col == 0 ? 0 : 1; + data[COLOUR_POS] = (byte) colour; + } + + } + + /** + * Constructor + */ + protected BaseCompoundFile() + { + } + +} + + + + Index: 3rdParty_sources/jexcelapi/jxl/biff/BuiltInFormat.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/BuiltInFormat.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/BuiltInFormat.java 17 Aug 2012 14:51:13 -0000 1.1 @@ -0,0 +1,176 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +import jxl.format.Format; + +/** + * The excel string for the various built in formats. Used to present + * the cell format information back to the user + * + * The difference between this class and the various format object contained + * in the jxl.write package is that this object contains the Excel strings, + * not their java equivalents + */ +final class BuiltInFormat implements Format, DisplayFormat +{ + /** + * The excel format string + */ + private String formatString; + + /** + * The index + */ + private int formatIndex; + + /** + * Constructor + * + * @param s the format string + * @param i the format index + */ + private BuiltInFormat(String s, int i) + { + formatIndex = i; + formatString = s; + } + + /** + * Accesses the excel format string which is applied to the cell + * Note that this is the string that excel uses, and not the java + * equivalent + * + * @return the cell format string + */ + public String getFormatString() + { + return formatString; + } + + /** + * Accessor for the index style of this format + * + * @return the index for this format + */ + public int getFormatIndex() + { + return formatIndex; + } + /** + * Accessor to see whether this format has been initialized + * + * @return TRUE if initialized, FALSE otherwise + */ + public boolean isInitialized() + { + return true; + } + /** + * Initializes this format with the specified index number + * + * @param pos the position of this format record in the workbook + */ + public void initialize(int pos) + { + } + + /** + * Accessor to determine whether or not this format is built in + * + * @return TRUE if this format is a built in format, FALSE otherwise + */ + public boolean isBuiltIn() + { + return true; + } + + /** + * Equals method + * + * @return TRUE if the two built in formats are equal, FALSE otherwise + */ + public boolean equals(Object o) + { + if (o == this) + { + return true; + } + + if (!(o instanceof BuiltInFormat)) + { + return false; + } + + BuiltInFormat bif = (BuiltInFormat) o; + return (formatIndex == bif.formatIndex); + } + + /** + * The list of built in formats + */ + public static BuiltInFormat[] builtIns = new BuiltInFormat[0x32]; + + // Populate the built ins + static + { + builtIns[0x0] = new BuiltInFormat("", 0); + builtIns[0x1] = new BuiltInFormat("0", 1); + builtIns[0x2] = new BuiltInFormat("0.00", 2); + builtIns[0x3] = new BuiltInFormat("#,##0", 3); + builtIns[0x4] = new BuiltInFormat("#,##0.00", 4); + builtIns[0x5] = new BuiltInFormat("($#,##0_);($#,##0)", 5); + builtIns[0x6] = new BuiltInFormat("($#,##0_);[Red]($#,##0)", 6); + builtIns[0x7] = new BuiltInFormat("($#,##0_);[Red]($#,##0)", 7); + builtIns[0x8] = new BuiltInFormat("($#,##0.00_);[Red]($#,##0.00)", 8); + builtIns[0x9] = new BuiltInFormat("0%", 9); + builtIns[0xa] = new BuiltInFormat("0.00%", 10); + builtIns[0xb] = new BuiltInFormat("0.00E+00", 11); + builtIns[0xc] = new BuiltInFormat("# ?/?", 12); + builtIns[0xd] = new BuiltInFormat("# ??/??", 13); + builtIns[0xe] = new BuiltInFormat("dd/mm/yyyy", 14); + builtIns[0xf] = new BuiltInFormat("d-mmm-yy", 15); + builtIns[0x10] = new BuiltInFormat("d-mmm", 16); + builtIns[0x11] = new BuiltInFormat("mmm-yy", 17); + builtIns[0x12] = new BuiltInFormat("h:mm AM/PM", 18); + builtIns[0x13] = new BuiltInFormat("h:mm:ss AM/PM", 19); + builtIns[0x14] = new BuiltInFormat("h:mm", 20); + builtIns[0x15] = new BuiltInFormat("h:mm:ss", 21); + builtIns[0x16] = new BuiltInFormat("m/d/yy h:mm", 22); + builtIns[0x25] = new BuiltInFormat("(#,##0_);(#,##0)", 0x25); + builtIns[0x26] = new BuiltInFormat("(#,##0_);[Red](#,##0)", 0x26); + builtIns[0x27] = new BuiltInFormat("(#,##0.00_);(#,##0.00)", 0x27); + builtIns[0x28] = new BuiltInFormat("(#,##0.00_);[Red](#,##0.00)", 0x28); + builtIns[0x29] = new BuiltInFormat + ("_(*#,##0_);_(*(#,##0);_(*\"-\"_);(@_)", 0x29); + builtIns[0x2a] = new BuiltInFormat + ("_($*#,##0_);_($*(#,##0);_($*\"-\"_);(@_)", 0x2a); + builtIns[0x2b] = new BuiltInFormat + ("_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);(@_)", 0x2b); + builtIns[0x2c] = new BuiltInFormat + ("_($* #,##0.00_);_($* (#,##0.00);_($* \"-\"??_);(@_)", 0x2c); + builtIns[0x2d] = new BuiltInFormat("mm:ss", 0x2d); + builtIns[0x2e] = new BuiltInFormat("[h]mm:ss", 0x2e); + builtIns[0x2f] = new BuiltInFormat("mm:ss.0", 0x2f); + builtIns[0x30] = new BuiltInFormat("##0.0E+0", 0x30); + builtIns[0x31] = new BuiltInFormat("@", 0x31); + } +} + Index: 3rdParty_sources/jexcelapi/jxl/biff/BuiltInName.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/BuiltInName.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/BuiltInName.java 17 Aug 2012 14:51:14 -0000 1.1 @@ -0,0 +1,120 @@ +/********************************************************************* +* +* Copyright (C) 2006 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +/** + * Enumeration of built in names + */ +public class BuiltInName +{ + /** + * The name + */ + private String name; + + /** + * The value + */ + private int value; + + /** + * The list of name + */ + private static BuiltInName[] builtInNames = new BuiltInName[0]; + /** + * Constructor + */ + private BuiltInName(String n, int v) + { + name = n; + value = v; + + BuiltInName[] oldnames = builtInNames; + builtInNames = new BuiltInName[oldnames.length + 1]; + System.arraycopy(oldnames, 0, builtInNames, 0, oldnames.length); + builtInNames[oldnames.length] = this; + } + + /** + * Accessor for the name + * + * @return the name + */ + public String getName() + { + return name; + } + + /** + * Accessor for the value + * + * @return the value + */ + public int getValue() + { + return value; + } + + /** + * Gets the built in name for the value + */ + public static BuiltInName getBuiltInName(int val) + { + BuiltInName ret = FILTER_DATABASE; + for (int i = 0 ; i < builtInNames.length; i++) + { + if (builtInNames[i].getValue() == val) + { + ret = builtInNames[i]; + } + } + return ret; + } + + // The list of built in names + public static final BuiltInName CONSOLIDATE_AREA = + new BuiltInName("Consolidate_Area", 0x0); + public static final BuiltInName AUTO_OPEN = + new BuiltInName("Auto_Open", 0x1); + public static final BuiltInName AUTO_CLOSE = + new BuiltInName("Auto_Open", 0x2); + public static final BuiltInName EXTRACT = + new BuiltInName("Extract", 0x3); + public static final BuiltInName DATABASE = + new BuiltInName("Database", 0x4); + public static final BuiltInName CRITERIA = + new BuiltInName("Criteria", 0x5); + public static final BuiltInName PRINT_AREA = + new BuiltInName("Print_Area", 0x6); + public static final BuiltInName PRINT_TITLES = + new BuiltInName("Print_Titles", 0x7); + public static final BuiltInName RECORDER = + new BuiltInName("Recorder", 0x8); + public static final BuiltInName DATA_FORM = + new BuiltInName("Data_Form", 0x9); + public static final BuiltInName AUTO_ACTIVATE = + new BuiltInName("Auto_Activate", 0xa); + public static final BuiltInName AUTO_DEACTIVATE = + new BuiltInName("Auto_Deactivate", 0xb); + public static final BuiltInName SHEET_TITLE = + new BuiltInName("Sheet_Title", 0xb); + public static final BuiltInName FILTER_DATABASE = + new BuiltInName("_FilterDatabase", 0xd); +} Index: 3rdParty_sources/jexcelapi/jxl/biff/BuiltInStyle.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/BuiltInStyle.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/BuiltInStyle.java 17 Aug 2012 14:51:14 -0000 1.1 @@ -0,0 +1,73 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +/** + * Represents a built in, rather than a user defined, style. + * This class is used by the FormattingRecords class when writing out the hard* + * coded styles + */ +class BuiltInStyle extends WritableRecordData +{ + /** + * The XF index of this style + */ + private int xfIndex; + /** + * The reference number of this style + */ + private int styleNumber; + + /** + * Constructor + * + * @param xfind the xf index of this style + * @param sn the style number of this style + */ + public BuiltInStyle(int xfind, int sn) + { + super(Type.STYLE); + + xfIndex = xfind; + styleNumber = sn; + } + + /** + * Abstract method implementation to get the raw byte data ready to write out + * + * @return The byte data + */ + public byte[] getData() + { + byte[] data = new byte[4]; + + IntegerHelper.getTwoBytes(xfIndex, data, 0); + + // Set the built in bit + data[1] |= 0x80; + + data[2] = (byte) styleNumber; + + // Set the outline level + data[3] = (byte) 0xff; + + return data; + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/ByteArray.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/ByteArray.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/ByteArray.java 17 Aug 2012 14:51:14 -0000 1.1 @@ -0,0 +1,117 @@ +/********************************************************************* +* +* Copyright (C) 2005 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +/** + * A growable array of bytes + */ +public class ByteArray +{ + /** + * The array grow size + */ + private int growSize; + + /** + * The current array + */ + private byte[] bytes; + + /** + * The current position + */ + private int pos; + + // The default grow size + private final static int defaultGrowSize = 1024; + + /** + * Constructor + */ + public ByteArray() + { + this(defaultGrowSize); + } + + /** + * Constructor + * + * @param gs + */ + public ByteArray(int gs) + { + growSize = gs; + bytes = new byte[defaultGrowSize]; + pos = 0; + } + + /** + * Adds a byte onto the array + * + * @param b the byte + */ + public void add(byte b) + { + checkSize(1); + bytes[pos] = b; + pos++; + } + + /** + * Adds an array of bytes onto the array + * + * @param b the array of bytes + */ + public void add(byte[] b) + { + checkSize(b.length); + System.arraycopy(b, 0, bytes, pos, b.length); + pos += b.length; + } + + /** + * Gets the complete array + * + * @return the array + */ + public byte[] getBytes() + { + byte[] returnArray = new byte[pos]; + System.arraycopy(bytes, 0, returnArray, 0, pos); + return returnArray; + } + + /** + * Checks to see if there is sufficient space left on the array. If not, + * then it grows the array + * + * @param sz the amount of bytes to add + */ + private void checkSize(int sz) + { + while (pos + sz >= bytes.length) + { + // Grow the array + byte[] newArray = new byte[bytes.length + growSize]; + System.arraycopy(bytes, 0, newArray, 0, pos); + bytes = newArray; + } + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/ByteData.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/ByteData.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/ByteData.java 17 Aug 2012 14:51:13 -0000 1.1 @@ -0,0 +1,34 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +/** + * Interface which provides a method for transferring chunks of binary + * data from one Excel file (read in) to another (written out) + */ +public interface ByteData +{ + /** + * Used when writing out records + * + * @return the full data to be included in the final compound file + */ + public byte[] getBytes(); +} Index: 3rdParty_sources/jexcelapi/jxl/biff/CellFinder.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/CellFinder.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/CellFinder.java 17 Aug 2012 14:51:14 -0000 1.1 @@ -0,0 +1,219 @@ +/********************************************************************** +* +* Copyright (C) 2008 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +import java.util.regex.Pattern; +import java.util.regex.Matcher; + +import jxl.Sheet; +import jxl.Cell; +import jxl.CellType; +import jxl.LabelCell; + +/** + * Refactorisation to provide more sophisticated find cell by contents + * functionality + */ +public class CellFinder +{ + private Sheet sheet; + + public CellFinder(Sheet s) + { + sheet = s; + } + + /** + * Gets the cell whose contents match the string passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform + * + * @param contents the string to match + * @param firstCol the first column within the range + * @param firstRow the first row of the range + * @param lastCol the last column within the range + * @param lastRow the last row within the range + * @param reverse indicates whether to perform a reverse search or not + * @return the Cell whose contents match the parameter, null if not found + */ + public Cell findCell(String contents, + int firstCol, + int firstRow, + int lastCol, + int lastRow, + boolean reverse) + { + Cell cell = null; + boolean found = false; + + int numCols = lastCol - firstCol; + int numRows = lastRow - firstRow; + + int row1 = reverse ? lastRow : firstRow; + int row2 = reverse ? firstRow : lastRow; + int col1 = reverse ? lastCol : firstCol; + int col2 = reverse ? firstCol : lastCol; + int inc = reverse ? -1 : 1; + + for (int i = 0; i <= numCols && found == false; i++) + { + for (int j = 0; j <= numRows && found == false; j++) + { + int curCol = col1 + i * inc; + int curRow = row1 + j * inc; + if (curCol < sheet.getColumns() && curRow < sheet.getRows()) + { + Cell c = sheet.getCell(curCol, curRow); + if (c.getType() != CellType.EMPTY) + { + if (c.getContents().equals(contents)) + { + cell = c; + found = true; + } + } + } + } + } + + return cell; + } + + /** + * Finds a cell within a given range of cells + * + * @param contents the string to match + * @return the Cell whose contents match the parameter, null if not found + */ + public Cell findCell(String contents) + { + Cell cell = null; + boolean found = false; + + for (int i = 0 ; i < sheet.getRows() && found == false; i++) + { + Cell[] row = sheet.getRow(i); + for (int j = 0 ; j < row.length && found == false; j++) + { + if (row[j].getContents().equals(contents)) + { + cell = row[j]; + found = true; + } + } + } + + return cell; + } + + /** + * Gets the cell whose contents match the regular expressionstring passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform + * + * @param pattern the regular expression string to match + * @param firstCol the first column within the range + * @param firstRow the first row of the range + * @param lastCol the last column within the range + * @param lastRow the last row within the range + * @param reverse indicates whether to perform a reverse search or not + * @return the Cell whose contents match the parameter, null if not found + */ + public Cell findCell(Pattern pattern, + int firstCol, + int firstRow, + int lastCol, + int lastRow, + boolean reverse) + { + Cell cell = null; + boolean found = false; + + int numCols = lastCol - firstCol; + int numRows = lastRow - firstRow; + + int row1 = reverse ? lastRow : firstRow; + int row2 = reverse ? firstRow : lastRow; + int col1 = reverse ? lastCol : firstCol; + int col2 = reverse ? firstCol : lastCol; + int inc = reverse ? -1 : 1; + + for (int i = 0; i <= numCols && found == false; i++) + { + for (int j = 0; j <= numRows && found == false; j++) + { + int curCol = col1 + i * inc; + int curRow = row1 + j * inc; + if (curCol < sheet.getColumns() && curRow < sheet.getRows()) + { + Cell c = sheet.getCell(curCol, curRow); + if (c.getType() != CellType.EMPTY) + { + Matcher m = pattern.matcher(c.getContents()); + if (m.matches()) + { + cell = c; + found = true; + } + } + } + } + } + + return cell; + } + + /** + * Gets the cell whose contents match the string passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform. This method differs + * from the findCell methods in that only cells with labels are + * queried - all numerical cells are ignored. This should therefore + * improve performance. + * + * @param contents the string to match + * @return the Cell whose contents match the paramter, null if not found + */ + public LabelCell findLabelCell(String contents) + { + LabelCell cell = null; + boolean found = false; + + for (int i = 0; i < sheet.getRows() && !found; i++) + { + Cell[] row = sheet.getRow(i); + for (int j = 0; j < row.length && !found; j++) + { + if ((row[j].getType() == CellType.LABEL || + row[j].getType() == CellType.STRING_FORMULA) && + row[j].getContents().equals(contents)) + { + cell = (LabelCell) row[j]; + found = true; + } + } + } + + return cell; + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/CellReferenceHelper.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/CellReferenceHelper.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/CellReferenceHelper.java 17 Aug 2012 14:51:14 -0000 1.1 @@ -0,0 +1,349 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +import common.Logger; + +import jxl.biff.formula.ExternalSheet; + +/** + * A helper to transform between excel cell references and + * sheet:column:row notation + * Because this function will be called when generating a string + * representation of a formula, the cell reference will merely + * be appened to the string buffer instead of returning a full + * blooded string, for performance reasons + */ +public final class CellReferenceHelper +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(CellReferenceHelper.class); + + /** + * The character which indicates whether a reference is fixed + */ + private static final char fixedInd='$'; + + /** + * The character which indicates the sheet name terminator + */ + private static final char sheetInd = '!'; + + /** + * Constructor to prevent instantiation + */ + private CellReferenceHelper() + { + } + + /** + * Gets the cell reference + * + * @param column + * @param row + * @param buf + */ + public static void getCellReference(int column, int row, StringBuffer buf) + { + // Put the column letter into the buffer + getColumnReference(column, buf); + + // Add the row into the buffer + buf.append(Integer.toString(row+1)); + } + + /** + * Overloaded method which prepends $ for absolute reference + * + * @param column + * @param colabs TRUE if the column reference is absolute + * @param row + * @param rowabs TRUE if the row reference is absolute + * @param buf + */ + public static void getCellReference(int column, boolean colabs, + int row, boolean rowabs, + StringBuffer buf) + { + if (colabs) + { + buf.append(fixedInd); + } + + // Put the column letter into the buffer + getColumnReference(column, buf); + + if (rowabs) + { + buf.append(fixedInd); + } + + // Add the row into the buffer + buf.append(Integer.toString(row+1)); + } + + /** + * Gets the column letter corresponding to the 0-based column number + * + * @param column the column number + * @return the letter for that column number + */ + public static String getColumnReference(int column) + { + StringBuffer buf = new StringBuffer(); + getColumnReference(column, buf); + return buf.toString(); + } + + /** + * Gets the column letter corresponding to the 0-based column number + * + * @param column the column number + * @param buf the string buffer in which to write the column letter + */ + public static void getColumnReference(int column, StringBuffer buf) + { + int v = column/26; + int r = column%26; + + StringBuffer tmp = new StringBuffer(); + while (v != 0) + { + char col = (char) ('A' + r) ; + + tmp.append(col); + + r = v%26 - 1; // subtract one because only rows >26 preceded by A + v = v/26; + } + + char col = (char) ('A' + r) ; + tmp.append(col); + + // Insert into the proper string buffer in reverse order + for (int i = tmp.length() - 1; i >= 0; i--) + { + buf.append(tmp.charAt(i)); + } + } + + /** + * Gets the fully qualified cell reference given the column, row + * external sheet reference etc + * + * @param sheet + * @param column + * @param row + * @param workbook + * @param buf + */ + public static void getCellReference + (int sheet, int column, int row, + ExternalSheet workbook, StringBuffer buf) + { + // buf.append('\''); - quotes now added by WorkbookParser + String name = workbook.getExternalSheetName(sheet); + buf.append(StringHelper.replace(name, "\'", "\'\'")); + // buf.append('\''); + buf.append(sheetInd); + getCellReference(column, row, buf); + } + + /** + * Gets the fully qualified cell reference given the column, row + * external sheet reference etc + * + * @param sheet + * @param column + * @param colabs TRUE if the column is an absolute reference + * @param row + * @param rowabs TRUE if the row is an absolute reference + * @param workbook + * @param buf + */ + public static void getCellReference + (int sheet, int column, boolean colabs, + int row, boolean rowabs, + ExternalSheet workbook, StringBuffer buf) + { + // WorkbookParser now appends quotes and escapes apostrophes + String name = workbook.getExternalSheetName(sheet); + buf.append(name); + buf.append(sheetInd); + getCellReference(column, colabs, row, rowabs, buf); + } + + /** + * Gets the fully qualified cell reference given the column, row + * external sheet reference etc + * + * @param sheet + * @param column + * @param row + * @param workbook + * @return the cell reference in the form 'Sheet 1'!A1 + */ + public static String getCellReference + (int sheet, int column, int row, + ExternalSheet workbook) + { + StringBuffer sb = new StringBuffer(); + getCellReference(sheet, column, row, workbook, sb); + return sb.toString(); + } + + + /** + * Gets the cell reference for the specified column and row + * + * @param column + * @param row + * @return + */ + public static String getCellReference(int column, int row) + { + StringBuffer buf = new StringBuffer(); + getCellReference(column, row, buf); + return buf.toString(); + } + + /** + * Gets the columnn number of the string cell reference + * + * @param s the string to parse + * @return the column portion of the cell reference + */ + public static int getColumn(String s) + { + int colnum = 0; + int numindex = getNumberIndex(s); + + String s2 = s.toUpperCase(); + + int startPos = s.lastIndexOf(sheetInd) + 1; + if (s.charAt(startPos) == fixedInd) + { + startPos++; + } + + int endPos = numindex; + if (s.charAt(numindex - 1) == fixedInd) + { + endPos--; + } + + for (int i = startPos; i < endPos ; i++) + { + + if (i != startPos) + { + colnum = (colnum+1) * 26; + } + colnum += (int) s2.charAt(i) - (int) 'A'; + } + + return colnum; + } + + /** + * Gets the row number of the cell reference + */ + public static int getRow(String s) + { + try + { + return (Integer.parseInt(s.substring(getNumberIndex(s))) - 1); + } + catch (NumberFormatException e) + { + logger.warn(e, e); + return 0xffff; + } + } + + /** + * Finds the position where the first number occurs in the string + */ + private static int getNumberIndex(String s) + { + // Find the position of the first number + boolean numberFound = false; + int pos = s.lastIndexOf(sheetInd) + 1; + char c = '\0'; + + while (!numberFound && pos < s.length() ) + { + c = s.charAt(pos); + + if (c >= '0' && c <= '9') + { + numberFound = true; + } + else + { + pos++; + } + } + + return pos; + } + + /** + * Sees if the column component is relative or not + * + * @param s + * @return TRUE if the column is relative, FALSE otherwise + */ + public static boolean isColumnRelative(String s) + { + return s.charAt(0) != fixedInd; + } + + /** + * Sees if the row component is relative or not + * + * @param s + * @return TRUE if the row is relative, FALSE otherwise + */ + public static boolean isRowRelative(String s) + { + return s.charAt(getNumberIndex(s) - 1) != fixedInd; + } + + /** + * Gets the sheet name from the cell reference string + * + * @param ref + * @return the sheet reference + */ + public static String getSheet(String ref) + { + int sheetPos = ref.lastIndexOf(sheetInd); + if (sheetPos == -1) + { + return ""; + } + + return ref.substring(0, sheetPos); + } + +} Index: 3rdParty_sources/jexcelapi/jxl/biff/CellReferenceHelper.java2 =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/CellReferenceHelper.java2,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/CellReferenceHelper.java2 17 Aug 2012 14:51:13 -0000 1.1 @@ -0,0 +1,209 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +import jxl.biff.formula.ExternalSheet; + +/** + * A helper to transform between excel cell references and + * sheet:column:row notation + * Because this function will be called when generating a string + * representation of a formula, the cell reference will merely + * be appened to the string buffer instead of returning a full + * blooded string, for performance reasons + */ +public final class CellReferenceHelper +{ + /** + * The character which indicates whether a reference is fixed + */ + private static final char fixedInd='$'; + + /** + * Constructor to prevent instantiation + */ + private CellReferenceHelper() + { + } + + public static void getCellReference(int column, int row, StringBuffer buf) + { + int v = column/26; + int r = column%26; + + StringBuffer tmp = new StringBuffer(); + while (v != 0) + { + char col = (char) ((int) 'A' + r) ; + + tmp.append(col); + + r = v%26 - 1; // subtract one because only rows >26 preceded by A + v = v/26; + } + + char col = (char) ((int) 'A' + r) ; + tmp.append(col); + + // Insert into the proper string buffer in reverse order + for (int i = tmp.length() - 1; i >= 0; i--) + { + buf.append(tmp.charAt(i)); + } + + buf.append(Integer.toString(row+1)); + } + + /** + * Gets the fully qualified cell reference given the column, row + * external sheet reference etc + * + * @param sheet + * @param column + * @param row + * @param workbook + * @param buf + */ + public static void getCellReference + (int sheet, int column, int row, + ExternalSheet workbook, StringBuffer buf) + { + buf.append(workbook.getExternalSheetName(sheet)); + buf.append('!'); + getCellReference(column, row, buf); + } + + /** + * Gets the cell reference for the specified column and row + * + * @param column + * @param row + * @return + */ + public static String getCellReference(int column, int row) + { + StringBuffer buf = new StringBuffer(); + getCellReference(column, row, buf); + return buf.toString(); + } + + /** + * Gets the columnn number of the string cell reference + * + * @param s the string to parse + * @return the column portion of the cell reference + */ + public static int getColumn(String s) + { + int colnum = 0; + int numindex = getNumberIndex(s); + + String s2 = s.toUpperCase(); + + int startPos = 0; + if (s.charAt(0) == fixedInd) + { + startPos = 1; + } + + int endPos = numindex; + if (s.charAt(numindex - 1) == fixedInd) + { + endPos--; + } + + for (int i = startPos; i < endPos ; i++) + { + + if (i != startPos) + { + colnum = (colnum+1) * 26; + } + colnum += (int) s2.charAt(i) - (int) 'A'; + } + + return colnum; + } + + /** + * Gets the row number of the cell reference + */ + public static int getRow(String s) + { + try + { + return (Integer.parseInt(s.substring(getNumberIndex(s))) - 1); + } + catch (NumberFormatException e) + { + System.err.println("Warning: " + e.toString()); + return 0xffff; + } + } + + /** + * Finds the position where the first number occurs in the string + */ + private static int getNumberIndex(String s) + { + // Find the position of the first number + boolean numberFound = false; + int pos = 0; + char c = '\0'; + + while (!numberFound && pos < s.length() ) + { + c = s.charAt(pos); + + if (c >= '0' && c <= '9') + { + numberFound = true; + } + else + { + pos++; + } + } + + return pos; + } + + /** + * Sees if the column component is relative or not + * + * @param s + * @return TRUE if the column is relative, FALSE otherwise + */ + public static boolean isColumnRelative(String s) + { + return s.charAt(0) != fixedInd; + } + + /** + * Sees if the row component is relative or not + * + * @param s + * @return TRUE if the row is relative, FALSE otherwise + */ + public static boolean isRowRelative(String s) + { + return s.charAt(getNumberIndex(s) - 1) != fixedInd; + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/ConditionalFormat.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/ConditionalFormat.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/ConditionalFormat.java 17 Aug 2012 14:51:13 -0000 1.1 @@ -0,0 +1,125 @@ +/********************************************************************* +* +* Copyright (C) 2007 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; + +import jxl.WorkbookSettings; +import jxl.biff.formula.ExternalSheet; +import jxl.write.biff.File; + +/** + * Class containing the CONDFMT and CF records for conditionally formatting + * a cell + */ +public class ConditionalFormat +{ + /** + * The range of the format + */ + private ConditionalFormatRangeRecord range; + + /** + * The format conditions + */ + private ArrayList conditions; + + /** + * Constructor + */ + public ConditionalFormat(ConditionalFormatRangeRecord cfrr) + { + range = cfrr; + conditions = new ArrayList(); + } + + /** + * Adds a condition + * + * @param cond the condition + */ + public void addCondition(ConditionalFormatRecord cond) + { + conditions.add(cond); + } + + /** + * Inserts a blank column into this spreadsheet. If the column is out of + * range of the columns in the sheet, then no action is taken + * + * @param col the column to insert + */ + public void insertColumn(int col) + { + range.insertColumn(col); + } + + /** + * Removes a column from this spreadsheet. If the column is out of range + * of the columns in the sheet, then no action is taken + * + * @param col the column to remove + */ + public void removeColumn(int col) + { + range.removeColumn(col); + } + + /** + * Removes a row from this spreadsheet. If the row is out of + * range of the columns in the sheet, then no action is taken + * + * @param row the row to remove + */ + public void removeRow(int row) + { + range.removeRow(row); + } + + /** + * Inserts a blank row into this spreadsheet. If the row is out of range + * of the rows in the sheet, then no action is taken + * + * @param row the row to insert + */ + public void insertRow(int row) + { + range.insertRow(row); + } + + /** + * Writes out the data validation + * + * @exception IOException + * @param outputFile the output file + */ + public void write(File outputFile) throws IOException + { + outputFile.write(range); + + for (Iterator i = conditions.iterator(); i.hasNext();) + { + ConditionalFormatRecord cfr = (ConditionalFormatRecord) i.next(); + outputFile.write(cfr); + } + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/ConditionalFormatRangeRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/ConditionalFormatRangeRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/ConditionalFormatRangeRecord.java 17 Aug 2012 14:51:14 -0000 1.1 @@ -0,0 +1,391 @@ +/********************************************************************* +* +* Copyright (C) 2007 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +import common.Logger; + +import jxl.read.biff.Record; + +/** + * Range information for conditional formatting + */ +public class ConditionalFormatRangeRecord extends WritableRecordData +{ + // The logger + private static Logger logger = + Logger.getLogger(ConditionalFormatRangeRecord.class); + + /** + * The enclosing range + */ + private Range enclosingRange; + + /** + * The discrete ranges + */ + private Range[] ranges; + + /** + * The number of ranges + */ + private int numRanges; + + /** + * Initialized flag + */ + private boolean initialized; + + /** + * Modified flag + */ + private boolean modified; + + /** + * The data + */ + private byte[] data; + + private static class Range + { + public int firstRow; + public int firstColumn; + public int lastRow; + public int lastColumn; + public boolean modified; + + public Range() + { + modified = false; + } + + /** + * Inserts a blank column into this spreadsheet. If the column is out of + * range of the columns in the sheet, then no action is taken + * + * @param col the column to insert + */ + public void insertColumn(int col) + { + if (col > lastColumn) + { + return; + } + + if (col <= firstColumn) + { + firstColumn++; + modified = true; + } + + if (col <= lastColumn) + { + lastColumn++; + modified = true; + } + } + + /** + * Removes a column from this spreadsheet. If the column is out of range + * of the columns in the sheet, then no action is taken + * + * @param col the column to remove + */ + public void removeColumn(int col) + { + if (col > lastColumn) + { + return; + } + + if (col < firstColumn) + { + firstColumn--; + modified = true; + } + + if (col <= lastColumn) + { + lastColumn--; + modified = true; + } + } + + /** + * Removes a row from this spreadsheet. If the row is out of + * range of the columns in the sheet, then no action is taken + * + * @param row the row to remove + */ + public void removeRow(int row) + { + if (row > lastRow) + { + return; + } + + if (row < firstRow) + { + firstRow--; + modified = true; + } + + if (row <= lastRow) + { + lastRow--; + modified = true; + } + } + + /** + * Inserts a blank row into this spreadsheet. If the row is out of range + * of the rows in the sheet, then no action is taken + * + * @param row the row to insert + */ + public void insertRow(int row) + { + if (row > lastRow) + { + return; + } + + if (row <= firstRow) + { + firstRow++; + modified = true; + } + + if (row <= lastRow) + { + lastRow++; + modified = true; + } + } + + } + + /** + * Constructor + */ + public ConditionalFormatRangeRecord(Record t) + { + super(t); + + initialized = false; + modified = false; + data = getRecord().getData(); + } + + /** + * Initialization function + */ + private void initialize() + { + enclosingRange = new Range(); + enclosingRange.firstRow = IntegerHelper.getInt(data[4], data[5]); + enclosingRange.lastRow = IntegerHelper.getInt(data[6], data[7]); + enclosingRange.firstColumn = IntegerHelper.getInt(data[8], data[9]); + enclosingRange.lastColumn = IntegerHelper.getInt(data[10], data[11]); + numRanges = IntegerHelper.getInt(data[12], data[13]); + ranges = new Range[numRanges]; + + int pos = 14; + + for (int i = 0; i < numRanges; i++) + { + ranges[i] = new Range(); + ranges[i].firstRow = IntegerHelper.getInt(data[pos], data[pos+1]); + ranges[i].lastRow = IntegerHelper.getInt(data[pos+2], data[pos+3]); + ranges[i].firstColumn = IntegerHelper.getInt(data[pos+4], data[pos+5]); + ranges[i].lastColumn = IntegerHelper.getInt(data[pos+6], data[pos+7]); + pos += 8; + } + + initialized = true; + } + + /** + * Inserts a blank column into this spreadsheet. If the column is out of + * range of the columns in the sheet, then no action is taken + * + * @param col the column to insert + */ + public void insertColumn(int col) + { + if (!initialized) + { + initialize(); + } + + enclosingRange.insertColumn(col); + if (enclosingRange.modified) + { + modified = true; + } + + for (int i = 0 ; i < ranges.length ; i++) + { + ranges[i].insertColumn(col); + + if (ranges[i].modified) + { + modified = true; + } + } + + return; + } + + /** + * Inserts a blank column into this spreadsheet. If the column is out of + * range of the columns in the sheet, then no action is taken + * + * @param col the column to insert + */ + public void removeColumn(int col) + { + if (!initialized) + { + initialize(); + } + + enclosingRange.removeColumn(col); + if (enclosingRange.modified) + { + modified = true; + } + + for (int i = 0 ; i < ranges.length ; i++) + { + ranges[i].removeColumn(col); + + if (ranges[i].modified) + { + modified = true; + } + } + + return; + } + + /** + * Removes a row from this spreadsheet. If the row is out of + * range of the columns in the sheet, then no action is taken + * + * @param row the row to remove + */ + public void removeRow(int row) + { + if (!initialized) + { + initialize(); + } + + enclosingRange.removeRow(row); + if (enclosingRange.modified) + { + modified = true; + } + + for (int i = 0 ; i < ranges.length ; i++) + { + ranges[i].removeRow(row); + + if (ranges[i].modified) + { + modified = true; + } + } + + return; + } + + /** + * Inserts a blank row into this spreadsheet. If the row is out of range + * of the rows in the sheet, then no action is taken + * + * @param row the row to insert + */ + public void insertRow(int row) + { + if (!initialized) + { + initialize(); + } + + enclosingRange.insertRow(row); + if (enclosingRange.modified) + { + modified = true; + } + + for (int i = 0 ; i < ranges.length ; i++) + { + ranges[i].insertRow(row); + + if (ranges[i].modified) + { + modified = true; + } + } + + return; + } + + + /** + * Retrieves the data for output to binary file + * + * @return the data to be written + */ + public byte[] getData() + { + if (!modified) + { + return data; + } + + byte[] d = new byte[14 + ranges.length * 8]; + + // Copy in the original information + System.arraycopy(data, 0, d, 0, 4); + + // Create the new range + IntegerHelper.getTwoBytes(enclosingRange.firstRow, d, 4); + IntegerHelper.getTwoBytes(enclosingRange.lastRow, d, 6); + IntegerHelper.getTwoBytes(enclosingRange.firstColumn, d, 8); + IntegerHelper.getTwoBytes(enclosingRange.lastColumn, d, 10); + + IntegerHelper.getTwoBytes(numRanges, d, 12); + + int pos = 14; + for (int i = 0 ; i < ranges.length ; i++) + { + IntegerHelper.getTwoBytes(ranges[i].firstRow, d, pos); + IntegerHelper.getTwoBytes(ranges[i].lastRow, d, pos+2); + IntegerHelper.getTwoBytes(ranges[i].firstColumn, d, pos+4); + IntegerHelper.getTwoBytes(ranges[i].lastColumn, d, pos+6); + pos += 8; + } + + return d; + } +} + Index: 3rdParty_sources/jexcelapi/jxl/biff/ConditionalFormatRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/ConditionalFormatRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/ConditionalFormatRecord.java 17 Aug 2012 14:51:13 -0000 1.1 @@ -0,0 +1,60 @@ +/********************************************************************* +* +* Copyright (C) 2007 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +import common.Logger; + +import jxl.read.biff.Record; + +/** + * The conditional format conditions + */ +public class ConditionalFormatRecord extends WritableRecordData +{ + // the logger + private static Logger logger = + Logger.getLogger(ConditionalFormatRecord.class); + + /** + * The data + */ + private byte[] data; + + /** + * Constructor + */ + public ConditionalFormatRecord(Record t) + { + super(t); + + data = getRecord().getData(); + } + + /** + * Retrieves the data for output to binary file + * + * @return the data to be written + */ + public byte[] getData() + { + return data; + } + +} Index: 3rdParty_sources/jexcelapi/jxl/biff/ContinueRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/ContinueRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/ContinueRecord.java 17 Aug 2012 14:51:14 -0000 1.1 @@ -0,0 +1,79 @@ +/********************************************************************* +* +* Copyright (C) 2004 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +import jxl.read.biff.Record; + +/** + * A continue record - only used explicitly in special circumstances, as + * the general continuation record is handled directly by the records + */ +public class ContinueRecord extends WritableRecordData +{ + /** + * The data + */ + private byte[] data; + + /** + * Constructor + * + * @param t the raw bytes + */ + public ContinueRecord(Record t) + { + super(t); + data = t.getData(); + } + + /** + * Constructor invoked when creating continue records + * + * @param d the data + */ + public ContinueRecord(byte[] d) + { + super(Type.CONTINUE); + data = d; + } + + /** + * Accessor for the binary data - used when copying + * + * @return the binary data + */ + public byte[] getData() + { + return data; + } + + /** + * Accessor for the record. Used when forcibly changing this record + * into another type, notably a drawing record, as sometimes Excel appears + * to switch to writing Continue records instead of MsoDrawing records + * + * @return the record + */ + public Record getRecord() + { + return super.getRecord(); + } + +} Index: 3rdParty_sources/jexcelapi/jxl/biff/CountryCode.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/CountryCode.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/CountryCode.java 17 Aug 2012 14:51:14 -0000 1.1 @@ -0,0 +1,167 @@ +/********************************************************************* +* +* Copyright (C) 2005 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +import common.Logger; + +/** + * Enumeration type for the excel country codes + */ +public class CountryCode +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(CountryCode.class); + + /** + * The country code + */ + private int value; + + /** + * The ISO 3166 two letter country mnemonic (as used by the Locale class) + */ + private String code; + + /** + * The long description + */ + private String description; + + /** + * The array of country codes + */ + private static CountryCode[] codes = new CountryCode[0]; + + /** + * Constructor + */ + private CountryCode(int v, String c, String d) + { + value = v; + code = c; + description = d; + + CountryCode[] newcodes = new CountryCode[codes.length+1]; + System.arraycopy(codes, 0, newcodes, 0, codes.length); + newcodes[codes.length] = this; + codes = newcodes; + } + + /** + * Constructor used to create an arbitrary code with a specified value. + * Doesn't add the latest value to the static array + */ + private CountryCode(int v) + { + value = v; + description = "Arbitrary"; + code = "??"; + } + + /** + * Accessor for the excel value + * + * @return the excel value + */ + public int getValue() + { + return value; + } + + /** + * Accessor for the string + * + * @return the two character iso 3166 string + */ + public String getCode() + { + return code; + } + + /** + * Gets the country code for the given two character mnemonic string + */ + public static CountryCode getCountryCode(String s) + { + if (s == null || s.length() != 2) + { + logger.warn("Please specify two character ISO 3166 country code"); + return USA; + } + + CountryCode code = UNKNOWN; + for (int i = 0 ; i < codes.length && code == UNKNOWN ; i++) + { + if (codes[i].code.equals(s)) + { + code = codes[i]; + } + } + + return code; + } + + /** + * Creates an arbitrary country code with the specified value. Used + * when copying sheets, and the country code isn't initialized as part + * of the static data below + */ + public static CountryCode createArbitraryCode(int i) + { + return new CountryCode(i); + } + + // The country codes + public final static CountryCode USA = new CountryCode(0x1, "US", "USA"); + public final static CountryCode CANADA = + new CountryCode(0x2, "CA", "Canada"); + public final static CountryCode GREECE = + new CountryCode(0x1e, "GR", "Greece"); + public final static CountryCode NETHERLANDS = + new CountryCode(0x1f, "NE", "Netherlands"); + public final static CountryCode BELGIUM = + new CountryCode(0x20, "BE", "Belgium"); + public final static CountryCode FRANCE = + new CountryCode(0x21, "FR", "France"); + public final static CountryCode SPAIN = new CountryCode(0x22, "ES", "Spain"); + public final static CountryCode ITALY = new CountryCode(0x27, "IT", "Italy"); + public final static CountryCode SWITZERLAND = + new CountryCode(0x29, "CH", "Switzerland"); + public final static CountryCode UK = + new CountryCode(0x2c, "UK", "United Kingdowm"); + public final static CountryCode DENMARK = + new CountryCode(0x2d, "DK", "Denmark"); + public final static CountryCode SWEDEN = + new CountryCode(0x2e, "SE", "Sweden"); + public final static CountryCode NORWAY = + new CountryCode(0x2f, "NO", "Norway"); + public final static CountryCode GERMANY = + new CountryCode(0x31, "DE", "Germany"); + public final static CountryCode PHILIPPINES = + new CountryCode(0x3f, "PH", "Philippines"); + public final static CountryCode CHINA = + new CountryCode(0x56, "CN", "China"); + public final static CountryCode INDIA = + new CountryCode(0x5b, "IN", "India"); + public final static CountryCode UNKNOWN = + new CountryCode(0xffff, "??", "Unknown"); +} Index: 3rdParty_sources/jexcelapi/jxl/biff/DVParser.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/DVParser.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/DVParser.java 17 Aug 2012 14:51:13 -0000 1.1 @@ -0,0 +1,924 @@ +/********************************************************************* +* +* Copyright (C) 2004 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +import common.Assert; +import common.Logger; + +import java.text.MessageFormat; +import java.text.DecimalFormat; +import java.util.Collection; +import java.util.Iterator; + +import jxl.WorkbookSettings; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; + +/** + * Class which parses the binary data associated with Data Validity (DV) + * setting + */ +public class DVParser +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(DVParser.class); + + // DV Type + public static class DVType + { + private int value; + private String desc; + + private static DVType[] types = new DVType[0]; + + DVType(int v, String d) + { + value = v; + desc = d; + DVType[] oldtypes = types; + types = new DVType[oldtypes.length+1]; + System.arraycopy(oldtypes, 0, types, 0, oldtypes.length); + types[oldtypes.length] = this; + } + + static DVType getType(int v) + { + DVType found = null; + for (int i = 0 ; i < types.length && found == null ; i++) + { + if (types[i].value == v) + { + found = types[i]; + } + } + return found; + } + + public int getValue() + { + return value; + } + + public String getDescription() + { + return desc; + } + } + + // Error Style + public static class ErrorStyle + { + private int value; + + private static ErrorStyle[] types = new ErrorStyle[0]; + + ErrorStyle(int v) + { + value = v; + ErrorStyle[] oldtypes = types; + types = new ErrorStyle[oldtypes.length+1]; + System.arraycopy(oldtypes, 0, types, 0, oldtypes.length); + types[oldtypes.length] = this; + } + + static ErrorStyle getErrorStyle(int v) + { + ErrorStyle found = null; + for (int i = 0 ; i < types.length && found == null ; i++) + { + if (types[i].value == v) + { + found = types[i]; + } + } + return found; + } + + public int getValue() + { + return value; + } + } + + // Conditions + public static class Condition + { + private int value; + private MessageFormat format; + + private static Condition[] types = new Condition[0]; + + Condition(int v, String pattern) + { + value = v; + format = new MessageFormat(pattern); + Condition[] oldtypes = types; + types = new Condition[oldtypes.length+1]; + System.arraycopy(oldtypes, 0, types, 0, oldtypes.length); + types[oldtypes.length] = this; + } + + static Condition getCondition(int v) + { + Condition found = null; + for (int i = 0 ; i < types.length && found == null ; i++) + { + if (types[i].value == v) + { + found = types[i]; + } + } + return found; + } + + public int getValue() + { + return value; + } + + public String getConditionString(String s1, String s2) + { + return format.format(new String[] {s1, s2}); + } + } + + // The values + public static final DVType ANY = new DVType(0, "any"); + public static final DVType INTEGER = new DVType(1, "int"); + public static final DVType DECIMAL = new DVType(2, "dec"); + public static final DVType LIST = new DVType(3, "list"); + public static final DVType DATE = new DVType(4, "date"); + public static final DVType TIME = new DVType(5, "time"); + public static final DVType TEXT_LENGTH = new DVType(6, "strlen"); + public static final DVType FORMULA = new DVType(7, "form"); + + // The error styles + public static final ErrorStyle STOP = new ErrorStyle(0); + public static final ErrorStyle WARNING = new ErrorStyle(1); + public static final ErrorStyle INFO = new ErrorStyle(2); + + // The conditions + public static final Condition BETWEEN = new Condition(0, "{0} <= x <= {1}"); + public static final Condition NOT_BETWEEN = + new Condition(1, "!({0} <= x <= {1}"); + public static final Condition EQUAL = new Condition(2, "x == {0}"); + public static final Condition NOT_EQUAL = new Condition(3, "x != {0}"); + public static final Condition GREATER_THAN = new Condition(4, "x > {0}"); + public static final Condition LESS_THAN = new Condition(5, "x < {0}"); + public static final Condition GREATER_EQUAL = new Condition(6, "x >= {0}"); + public static final Condition LESS_EQUAL = new Condition(7, "x <= {0}"); + + // The masks + private static final int STRING_LIST_GIVEN_MASK = 0x80; + private static final int EMPTY_CELLS_ALLOWED_MASK = 0x100; + private static final int SUPPRESS_ARROW_MASK = 0x200; + private static final int SHOW_PROMPT_MASK = 0x40000; + private static final int SHOW_ERROR_MASK = 0x80000; + + // The decimal format + private static DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#.#"); + + // The maximum string length for a data validation list + private static final int MAX_VALIDATION_LIST_LENGTH = 254; + + /** + * The type + */ + private DVType type; + + /** + * The error style + */ + private ErrorStyle errorStyle; + + /** + * The condition + */ + private Condition condition; + + /** + * String list option + */ + private boolean stringListGiven; + + /** + * Empty cells allowed + */ + private boolean emptyCellsAllowed; + + /** + * Suppress arrow + */ + private boolean suppressArrow; + + /** + * Show prompt + */ + private boolean showPrompt; + + /** + * Show error + */ + private boolean showError; + + /** + * The title of the prompt box + */ + private String promptTitle; + + /** + * The title of the error box + */ + private String errorTitle; + + /** + * The text of the prompt box + */ + private String promptText; + + /** + * The text of the error box + */ + private String errorText; + + /** + * The first formula + */ + private FormulaParser formula1; + + /** + * The first formula string + */ + private String formula1String; + + /** + * The second formula + */ + private FormulaParser formula2; + + /** + * The second formula string + */ + private String formula2String; + + /** + * The column number of the cell at the top left of the range + */ + private int column1; + + /** + * The row number of the cell at the top left of the range + */ + private int row1; + + /** + * The column index of the cell at the bottom right + */ + private int column2; + + /** + * The row index of the cell at the bottom right + */ + private int row2; + + /** + * Constructor + */ + public DVParser(byte[] data, + ExternalSheet es, + WorkbookMethods nt, + WorkbookSettings ws) throws FormulaException + { + Assert.verify(nt != null); + + int options = IntegerHelper.getInt(data[0], data[1], data[2], data[3]); + + int typeVal = options & 0xf; + type = DVType.getType(typeVal); + + int errorStyleVal = (options & 0x70) >> 4; + errorStyle = ErrorStyle.getErrorStyle(errorStyleVal); + + int conditionVal = (options & 0xf00000) >> 20; + condition = Condition.getCondition(conditionVal); + + stringListGiven = (options & STRING_LIST_GIVEN_MASK) != 0; + emptyCellsAllowed = (options & EMPTY_CELLS_ALLOWED_MASK) != 0; + suppressArrow = (options & SUPPRESS_ARROW_MASK) != 0; + showPrompt = (options & SHOW_PROMPT_MASK) != 0; + showError = (options & SHOW_ERROR_MASK) != 0; + + int pos = 4; + int length = IntegerHelper.getInt(data[pos], data[pos+1]); + if (length > 0 && data[pos + 2] == 0) + { + promptTitle = StringHelper.getString(data, length, pos + 3, ws); + pos += length + 3; + } + else if (length > 0) + { + promptTitle = StringHelper.getUnicodeString(data, length, pos + 3); + pos += length * 2 + 3; + } + else + { + pos += 3; + } + + length = IntegerHelper.getInt(data[pos], data[pos+1]); + if (length > 0 && data[pos + 2] == 0) + { + errorTitle = StringHelper.getString(data, length, pos + 3, ws); + pos += length + 3; + } + else if (length > 0) + { + errorTitle = StringHelper.getUnicodeString(data, length, pos + 3); + pos += length * 2 + 3; + } + else + { + pos += 3; + } + + length = IntegerHelper.getInt(data[pos], data[pos+1]); + if (length > 0 && data[pos + 2] == 0) + { + promptText = StringHelper.getString(data, length, pos + 3, ws); + pos += length + 3; + } + else if (length > 0) + { + promptText = StringHelper.getUnicodeString(data, length, pos + 3); + pos += length * 2 + 3; + } + else + { + pos += 3; + } + + length = IntegerHelper.getInt(data[pos], data[pos+1]); + if (length > 0 && data[pos + 2] == 0) + { + errorText = StringHelper.getString(data, length, pos + 3, ws); + pos += length + 3; + } + else if (length > 0) + { + errorText = StringHelper.getUnicodeString(data, length, pos + 3); + pos += length * 2 + 3; + } + else + { + pos += 3; + } + + int formula1Length = IntegerHelper.getInt(data[pos], data[pos+1]); + pos += 4; + int formula1Pos = pos; + pos += formula1Length; + + int formula2Length = IntegerHelper.getInt(data[pos], data[pos+1]); + pos += 4; + int formula2Pos = pos; + pos += formula2Length; + + pos += 2; + + row1 = IntegerHelper.getInt(data[pos], data[pos+1]); + pos += 2; + + row2 = IntegerHelper.getInt(data[pos], data[pos+1]); + pos += 2; + + column1 = IntegerHelper.getInt(data[pos], data[pos+1]); + pos += 2; + + column2 = IntegerHelper.getInt(data[pos], data[pos+1]); + pos += 2; + + // Do the formulas + + // First, create a temporary blank cell for any formula relative + // references + EmptyCell tmprt = new EmptyCell(column1, row1); + + if (formula1Length != 0) + { + byte[] tokens = new byte[formula1Length]; + System.arraycopy(data, formula1Pos, tokens, 0, formula1Length); + formula1 = new FormulaParser(tokens, tmprt, es, nt,ws); + formula1.parse(); + } + + if (formula2Length != 0) + { + byte[] tokens = new byte[formula2Length]; + System.arraycopy(data, formula2Pos, tokens, 0, formula2Length); + formula2 = new FormulaParser(tokens, tmprt, es, nt, ws); + formula2.parse(); + } + } + + /** + * Constructor called when creating a data validation from the API + */ + public DVParser(Collection strings) + { + type = LIST; + errorStyle = STOP; + condition = BETWEEN; + + // the options + stringListGiven = true; + emptyCellsAllowed = true; + suppressArrow = false; + showPrompt = true; + showError = true; + + promptTitle = "\0"; + errorTitle = "\0"; + promptText = "\0"; + errorText = "\0"; + if (strings.size() == 0) + { + logger.warn("no validation strings - ignoring"); + } + + Iterator i = strings.iterator(); + StringBuffer formulaString = new StringBuffer(); + + formulaString.append(i.next().toString()); + while (i.hasNext()) + { + formulaString.append('\0'); + formulaString.append(' '); + formulaString.append(i.next().toString()); + } + + // If the formula string exceeds + // the maximum validation list length, then truncate and stop there + if (formulaString.length() > MAX_VALIDATION_LIST_LENGTH) + { + logger.warn("Validation list exceeds maximum number of characters - " + + "truncating"); + formulaString.delete(MAX_VALIDATION_LIST_LENGTH, + formulaString.length()); + } + + // Put the string in quotes + formulaString.insert(0, '\"'); + formulaString.append('\"'); + formula1String = formulaString.toString(); + } + + /** + * Constructor called when creating a data validation from the API + */ + public DVParser(String namedRange) + { + type = LIST; + errorStyle = STOP; + condition = BETWEEN; + + // the options + stringListGiven = false; + emptyCellsAllowed = true; + suppressArrow = false; + showPrompt = true; + showError = true; + + promptTitle = "\0"; + errorTitle = "\0"; + promptText = "\0"; + errorText = "\0"; + formula1String = namedRange; + } + + /** + * Constructor called when creating a data validation from the API + */ + public DVParser(int c1, int r1, int c2, int r2) + { + type = LIST; + errorStyle = STOP; + condition = BETWEEN; + + // the options + stringListGiven = false; + emptyCellsAllowed = true; + suppressArrow = false; + showPrompt = true; + showError = true; + + promptTitle = "\0"; + errorTitle = "\0"; + promptText = "\0"; + errorText = "\0"; + StringBuffer formulaString = new StringBuffer(); + CellReferenceHelper.getCellReference(c1,r1,formulaString); + formulaString.append(':'); + CellReferenceHelper.getCellReference(c2,r2,formulaString); + formula1String = formulaString.toString(); + } + + /** + * Constructor called when creating a data validation from the API + */ + public DVParser(double val1, double val2, Condition c) + { + type = DECIMAL; + errorStyle = STOP; + condition = c; + + // the options + stringListGiven = false; + emptyCellsAllowed = true; + suppressArrow = false; + showPrompt = true; + showError = true; + + promptTitle = "\0"; + errorTitle = "\0"; + promptText = "\0"; + errorText = "\0"; + formula1String = DECIMAL_FORMAT.format(val1); + + if (!Double.isNaN(val2)) + { + formula2String = DECIMAL_FORMAT.format(val2); + } + } + + /** + * Constructor called when doing a cell deep copy + */ + public DVParser(DVParser copy) + { + type = copy.type; + errorStyle = copy.errorStyle; + condition = copy.condition; + stringListGiven = copy.stringListGiven; + emptyCellsAllowed = copy.emptyCellsAllowed; + suppressArrow = copy.suppressArrow; + showPrompt = copy.showPrompt; + showError = copy.showError; + promptTitle = copy.promptTitle; + promptText = copy.promptText; + errorTitle = copy.errorTitle; + errorText = copy.errorText; + + // Don't copy the formula parsers - just take their string equivalents + if (copy.formula1String != null) + { + formula1String = copy.formula1String; + formula2String = copy.formula2String; + } + else + { + try + { + formula1String = copy.formula1.getFormula(); + formula2String = (copy.formula2 != null) ? + copy.formula2.getFormula() : null; + } + catch (FormulaException e) + { + logger.warn("Cannot parse validation formula: " + e.getMessage()); + } + } + // Don't copy the cell references - these will be added later + } + + /** + * Gets the data + */ + public byte[] getData() + { + // Compute the length of the data + byte[] f1Bytes = formula1 != null ? formula1.getBytes() : new byte[0]; + byte[] f2Bytes = formula2 != null ? formula2.getBytes() : new byte[0]; + int dataLength = + 4 + // the options + promptTitle.length() * 2 + 2 + // the prompt title + errorTitle.length() * 2 + 2 + // the error title + promptText.length() * 2 + 2 + // the prompt text + errorText.length() * 2 + 2 + // the error text + f1Bytes.length + 2 + // first formula + f2Bytes.length + 2 + // second formula + + 4 + // unused bytes + 10; // cell range + + byte[] data = new byte[dataLength]; + + // The position + int pos = 0; + + // The options + int options = 0; + options |= type.getValue(); + options |= errorStyle.getValue() << 4; + options |= condition.getValue() << 20; + + if (stringListGiven) + { + options |= STRING_LIST_GIVEN_MASK; + } + + if (emptyCellsAllowed) + { + options |= EMPTY_CELLS_ALLOWED_MASK; + } + + if (suppressArrow) + { + options |= SUPPRESS_ARROW_MASK; + } + + if (showPrompt) + { + options |= SHOW_PROMPT_MASK; + } + + if (showError) + { + options |= SHOW_ERROR_MASK; + } + + // The text + IntegerHelper.getFourBytes(options, data, pos); + pos += 4; + + IntegerHelper.getTwoBytes(promptTitle.length(), data, pos); + pos += 2; + + StringHelper.getUnicodeBytes(promptTitle, data, pos); + pos += promptTitle.length() * 2; + + IntegerHelper.getTwoBytes(errorTitle.length(), data, pos); + pos += 2; + + StringHelper.getUnicodeBytes(errorTitle, data, pos); + pos += errorTitle.length() * 2; + + IntegerHelper.getTwoBytes(promptText.length(), data, pos); + pos += 2; + + StringHelper.getUnicodeBytes(promptText, data, pos); + pos += promptText.length() * 2; + + IntegerHelper.getTwoBytes(errorText.length(), data, pos); + pos += 2; + + StringHelper.getUnicodeBytes(errorText, data, pos); + pos += errorText.length() * 2; + + // Formula 1 + IntegerHelper.getTwoBytes(f1Bytes.length, data, pos); + pos += 4; + + System.arraycopy(f1Bytes, 0, data, pos, f1Bytes.length); + pos += f1Bytes.length; + + // Formula 2 + IntegerHelper.getTwoBytes(f2Bytes.length, data, pos); + pos += 4; + + System.arraycopy(f2Bytes, 0, data, pos, f2Bytes.length); + pos += f2Bytes.length; + + // The cell ranges + IntegerHelper.getTwoBytes(1, data, pos); + pos += 2; + + IntegerHelper.getTwoBytes(row1, data, pos); + pos += 2; + + IntegerHelper.getTwoBytes(row2, data, pos); + pos += 2; + + IntegerHelper.getTwoBytes(column1, data, pos); + pos += 2; + + IntegerHelper.getTwoBytes(column2, data, pos); + pos += 2; + + return data; + } + + /** + * Inserts a row + * + * @param row the row to insert + */ + public void insertRow(int row) + { + if (formula1 != null) + { + formula1.rowInserted(0, row, true); + } + + if (formula2 != null) + { + formula2.rowInserted(0, row, true); + } + + if (row1 >= row) + { + row1++; + } + + if (row2 >= row) + { + row2++; + } + } + + /** + * Inserts a column + * + * @param col the column to insert + */ + public void insertColumn(int col) + { + if (formula1 != null) + { + formula1.columnInserted(0, col, true); + } + + if (formula2 != null) + { + formula2.columnInserted(0, col, true); + } + + if (column1 >= col) + { + column1++; + } + + if (column2 >= col) + { + column2++; + } + } + + /** + * Removes a row + * + * @param row the row to insert + */ + public void removeRow(int row) + { + if (formula1 != null) + { + formula1.rowRemoved(0, row, true); + } + + if (formula2 != null) + { + formula2.rowRemoved(0, row, true); + } + + if (row1 > row) + { + row1--; + } + + if (row2 >= row) + { + row2--; + } + } + + /** + * Removes a column + * + * @param col the row to remove + */ + public void removeColumn(int col) + { + if (formula1 != null) + { + formula1.columnRemoved(0, col, true); + } + + if (formula2 != null) + { + formula2.columnRemoved(0, col, true); + } + + if (column1 > col) + { + column1--; + } + + if (column2 >= col) + { + column2--; + } + } + + /** + * Accessor for first column + * + * @return the first column + */ + public int getFirstColumn() + { + return column1; + } + + /** + * Accessor for the last column + * + * @return the last column + */ + public int getLastColumn() + { + return column2; + } + + /** + * Accessor for first row + * + * @return the first row + */ + public int getFirstRow() + { + return row1; + } + + /** + * Accessor for the last row + * + * @return the last row + */ + public int getLastRow() + { + return row2; + } + + /** + * Gets the formula present in the validation + * + * @return the validation formula as a string + * @exception FormulaException + */ + String getValidationFormula() throws FormulaException + { + if (type == LIST) + { + return formula1.getFormula(); + } + + String s1 = formula1.getFormula(); + String s2 = formula2 != null ? formula2.getFormula() : null; + return condition.getConditionString(s1, s2) + + "; x " + type.getDescription(); + } + + /** + * Called by the cell value when the cell features are added to the sheet + */ + public void setCell(int col, int row, + ExternalSheet es, + WorkbookMethods nt, + WorkbookSettings ws) throws FormulaException + { + row1 = row; + row2 = row; + column1 = col; + column2 = col; + + formula1 = new FormulaParser(formula1String, + es, nt, ws); + formula1.parse(); + + if (formula2String != null) + { + formula2 = new FormulaParser(formula2String, + es, nt, ws); + formula2.parse(); + } + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/DValParser.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/DValParser.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/DValParser.java 17 Aug 2012 14:51:14 -0000 1.1 @@ -0,0 +1,162 @@ +/********************************************************************* +* +* Copyright (C) 2004 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +import common.Logger; + +import jxl.biff.IntegerHelper; + +/** + * Class which parses the binary data associated with Data Validity (DVal) + * setting + */ +public class DValParser +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(DValParser.class); + + // The option masks + private static int PROMPT_BOX_VISIBLE_MASK = 0x1; + private static int PROMPT_BOX_AT_CELL_MASK = 0x2; + private static int VALIDITY_DATA_CACHED_MASK = 0x4; + + /** + * Prompt box visible + */ + private boolean promptBoxVisible; + + /** + * Empty cells allowed + */ + private boolean promptBoxAtCell; + + /** + * Cell validity data cached in following DV records + */ + private boolean validityDataCached; + + /** + * The number of following DV records + */ + private int numDVRecords; + + /** + * The object id of the associated down arrow + */ + private int objectId; + + /** + * Constructor + */ + public DValParser(byte[] data) + { + int options = IntegerHelper.getInt(data[0], data[1]); + + promptBoxVisible = (options & PROMPT_BOX_VISIBLE_MASK) != 0; + promptBoxAtCell = (options & PROMPT_BOX_AT_CELL_MASK) != 0; + validityDataCached = (options & VALIDITY_DATA_CACHED_MASK) != 0; + + objectId = IntegerHelper.getInt(data[10], data[11], data[12], data[13]); + numDVRecords = IntegerHelper.getInt(data[14], data[15], + data[16], data[17]); + } + + /** + * Constructor + */ + public DValParser(int objid, int num) + { + objectId = objid; + numDVRecords = num; + validityDataCached = true; + } + + /** + * Gets the data + */ + public byte[] getData() + { + byte[] data = new byte[18]; + + int options = 0; + + if (promptBoxVisible) + { + options |= PROMPT_BOX_VISIBLE_MASK; + } + + if (promptBoxAtCell) + { + options |= PROMPT_BOX_AT_CELL_MASK; + } + + if (validityDataCached) + { + options |= VALIDITY_DATA_CACHED_MASK; + } + + IntegerHelper.getTwoBytes(options, data, 0); + + IntegerHelper.getFourBytes(objectId, data, 10); + + IntegerHelper.getFourBytes(numDVRecords, data, 14); + + return data; + } + + /** + * Called when a remove row or column results in one of DV records being + * removed + */ + public void dvRemoved() + { + numDVRecords--; + } + + /** + * Accessor for the number of DV records + * + * @return the number of DV records for this list + */ + public int getNumberOfDVRecords() + { + return numDVRecords; + } + + /** + * Accessor for the object id + * + * @return the object id + */ + public int getObjectId() + { + return objectId; + } + + /** + * Called when adding a DV record on a copied DVal + */ + public void dvAdded() + { + numDVRecords++; + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/DataValidation.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/DataValidation.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/DataValidation.java 17 Aug 2012 14:51:13 -0000 1.1 @@ -0,0 +1,320 @@ +/********************************************************************* +* +* Copyright (C) 2004 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; + +import common.Assert; +import common.Logger; + +import jxl.WorkbookSettings; +import jxl.biff.formula.ExternalSheet; +import jxl.write.biff.File; + + +/** + * Class which encapsulates a data validation (typically in the form of a + * dropdown list box) + */ +public class DataValidation +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(DataValidation.class); + + /** + * The data validity list + */ + private DataValidityListRecord validityList; + + /** + * The data validity record + */ + private ArrayList validitySettings; + + /** + * Handle to the workbook + */ + private WorkbookMethods workbook; + + /** + * Handle to the external sheet + */ + private ExternalSheet externalSheet; + + /** + * Handle to the workbook settings + */ + private WorkbookSettings workbookSettings; + + /** + * The object id of the combo box used for drop downs + */ + private int comboBoxObjectId; + + /** + * Indicates whether this was copied + */ + private boolean copied; + + public static final int DEFAULT_OBJECT_ID = 0xffffffff; + + /** + * Constructor + */ + public DataValidation(DataValidityListRecord dvlr) + { + validityList = dvlr; + validitySettings = new ArrayList(validityList.getNumberOfSettings()); + copied = false; + } + + /** + * Constructor used to create writable data validations + */ + public DataValidation(int objId, + ExternalSheet es, + WorkbookMethods wm, + WorkbookSettings ws ) + { + workbook = wm; + externalSheet = es; + workbookSettings = ws; + validitySettings = new ArrayList(); + comboBoxObjectId = objId; + copied = false; + } + + /** + * Copy constructor used to copy from read to write + */ + public DataValidation(DataValidation dv, + ExternalSheet es, + WorkbookMethods wm, + WorkbookSettings ws ) + { + workbook = wm; + externalSheet = es; + workbookSettings = ws; + copied = true; + validityList = new DataValidityListRecord(dv.getDataValidityList()); + + validitySettings = new ArrayList(); + DataValiditySettingsRecord[] settings = dv.getDataValiditySettings(); + + for (int i = 0; i < settings.length ; i++) + { + validitySettings.add(new DataValiditySettingsRecord(settings[i], + externalSheet, + workbook, + workbookSettings)); + } + } + + /** + * Adds a new settings object to this data validation + */ + public void add(DataValiditySettingsRecord dvsr) + { + validitySettings.add(dvsr); + dvsr.setDataValidation(this); + + if (copied) + { + // adding a writable dv record to a copied validity list + Assert.verify(validityList != null); + validityList.dvAdded(); + } + } + + /** + * Accessor for the validity list. Used when copying sheets + */ + public DataValidityListRecord getDataValidityList() + { + return validityList; + } + + /** + * Accessor for the validity settings. Used when copying sheets + */ + public DataValiditySettingsRecord[] getDataValiditySettings() + { + DataValiditySettingsRecord[] dvlr = new DataValiditySettingsRecord[0]; + return (DataValiditySettingsRecord[]) validitySettings.toArray(dvlr); + } + + /** + * Writes out the data validation + * + * @exception IOException + * @param outputFile the output file + */ + public void write(File outputFile) throws IOException + { + if (validityList == null) + { + DValParser dvp = new DValParser(comboBoxObjectId, + validitySettings.size()); + validityList = new DataValidityListRecord(dvp); + } + + if (!validityList.hasDVRecords()) + { + return; + } + + outputFile.write(validityList); + + for (Iterator i = validitySettings.iterator(); i.hasNext() ; ) + { + DataValiditySettingsRecord dvsr = (DataValiditySettingsRecord) i.next(); + outputFile.write(dvsr); + } + } + + /** + * Inserts a row + * + * @param row the inserted row + */ + public void insertRow(int row) + { + for (Iterator i = validitySettings.iterator(); i.hasNext() ; ) + { + DataValiditySettingsRecord dv = (DataValiditySettingsRecord) i.next(); + dv.insertRow(row); + } + } + + /** + * Inserts a row + * + * @param row the inserted row + */ + public void removeRow(int row) + { + for (Iterator i = validitySettings.iterator(); i.hasNext() ; ) + { + DataValiditySettingsRecord dv = (DataValiditySettingsRecord) i.next(); + + if (dv.getFirstRow() == row && dv.getLastRow() == row) + { + i.remove(); + validityList.dvRemoved(); + } + else + { + dv.removeRow(row); + } + } + } + + /** + * Inserts a column + * + * @param col the inserted column + */ + public void insertColumn(int col) + { + for (Iterator i = validitySettings.iterator(); i.hasNext() ; ) + { + DataValiditySettingsRecord dv = (DataValiditySettingsRecord) i.next(); + dv.insertColumn(col); + } + } + + /** + * Removes a column + * + * @param col the inserted column + */ + public void removeColumn(int col) + { + for (Iterator i = validitySettings.iterator(); i.hasNext() ; ) + { + DataValiditySettingsRecord dv = (DataValiditySettingsRecord) i.next(); + + if (dv.getFirstColumn() == col && dv.getLastColumn() == col) + { + i.remove(); + validityList.dvRemoved(); + } + else + { + dv.removeColumn(col); + } + } + } + + /** + * Removes the data validation for a specific cell + * + * @param col the column + * @param row the row + */ + public void removeDataValidation (int col, int row) + { + for (Iterator i = validitySettings.iterator(); i.hasNext() ; ) + { + DataValiditySettingsRecord dv = (DataValiditySettingsRecord) i.next(); + + if (dv.getFirstColumn() == col && dv.getLastColumn() == col && + dv.getFirstRow() == row && dv.getLastRow() == row) + { + i.remove(); + validityList.dvRemoved(); + break; + } + } + } + + /** + * Used during the copy process to retrieve the validity settings for + * a particular cell + */ + public DataValiditySettingsRecord getDataValiditySettings(int col, int row) + { + boolean found = false; + DataValiditySettingsRecord foundRecord = null; + for (Iterator i = validitySettings.iterator(); i.hasNext() && !found;) + { + DataValiditySettingsRecord dvsr = (DataValiditySettingsRecord) i.next(); + if (dvsr.getFirstColumn() == col && dvsr.getFirstRow() == row) + { + found = true; + foundRecord = dvsr; + } + } + + return foundRecord; + } + + /** + * Accessor for the combo box, used when copying sheets + */ + public int getComboBoxObjectId() + { + return comboBoxObjectId; + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/DataValidityListRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/DataValidityListRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/DataValidityListRecord.java 17 Aug 2012 14:51:13 -0000 1.1 @@ -0,0 +1,163 @@ +/********************************************************************* +* +* Copyright (C) 2004 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +import jxl.read.biff.Record; + +/** + * Record containing the list of data validation settings for a given sheet + */ +public class DataValidityListRecord extends WritableRecordData +{ + + /** + * The number of settings records associated with this list + */ + private int numSettings; + + /** + * The object id of the associated down arrow + */ + private int objectId; + + /** + * The dval parser + */ + private DValParser dvalParser; + + /** + * The data + */ + private byte[] data; + + /** + * Constructor + */ + public DataValidityListRecord(Record t) + { + super(t); + + data = getRecord().getData(); + objectId = IntegerHelper.getInt(data[10], data[11], data[12], data[13]); + numSettings = IntegerHelper.getInt(data[14], data[15], data[16], data[17]); + } + + /** + * Constructor called when generating a data validity list from the API + */ + public DataValidityListRecord(DValParser dval) + { + super(Type.DVAL); + + dvalParser = dval; + } + + /** + * Copy constructor + * + * @param dvlr the record copied from a read only sheet + */ + DataValidityListRecord(DataValidityListRecord dvlr) + { + super(Type.DVAL); + + data = dvlr.getData(); + } + + /** + * Accessor for the number of settings records associated with this list + */ + int getNumberOfSettings() + { + return numSettings; + } + + /** + * Retrieves the data for output to binary file + * + * @return the data to be written + */ + public byte[] getData() + { + if (dvalParser == null) + { + return data; + } + + return dvalParser.getData(); + } + + /** + * Called when a remove row or column results in one of DV records being + * removed + */ + void dvRemoved() + { + if (dvalParser == null) + { + dvalParser = new DValParser(data); + } + + dvalParser.dvRemoved(); + } + + /** + * Called when a writable DV record is added to a copied validity list + */ + void dvAdded() + { + if (dvalParser == null) + { + dvalParser = new DValParser(data); + } + + dvalParser.dvAdded(); + } + + /** + * Accessor for the number of DV records + * + * @return the number of DV records for this list + */ + public boolean hasDVRecords() + { + if (dvalParser == null) + { + return true; + } + + return dvalParser.getNumberOfDVRecords() > 0; + } + + /** + * Accessor for the object id + * + * @return the object id + */ + public int getObjectId() + { + if (dvalParser == null) + { + return objectId; + } + + return dvalParser.getObjectId(); + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/DataValiditySettingsRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/DataValiditySettingsRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/DataValiditySettingsRecord.java 17 Aug 2012 14:51:13 -0000 1.1 @@ -0,0 +1,322 @@ +/********************************************************************* +* +* Copyright (C) 2004 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +import common.Assert; +import common.Logger; + +import jxl.WorkbookSettings; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaException; +import jxl.read.biff.Record; + +/** + * Data validity settings + */ +public class DataValiditySettingsRecord extends WritableRecordData +{ + /** + * The logger + */ + private static Logger logger = + Logger.getLogger(DataValiditySettingsRecord.class); + + /** + * The binary data + */ + private byte[] data; + + /** + * The reader + */ + private DVParser dvParser; + + /** + * Handle to the workbook + */ + private WorkbookMethods workbook; + + /** + * Handle to the externalSheet + */ + private ExternalSheet externalSheet; + + /** + * Handle to the workbook settings + */ + private WorkbookSettings workbookSettings; + + /** + * Handle to the data validation record + */ + private DataValidation dataValidation; + + /** + * Constructor + */ + public DataValiditySettingsRecord(Record t, + ExternalSheet es, + WorkbookMethods wm, + WorkbookSettings ws) + { + super(t); + + data = t.getData(); + externalSheet = es; + workbook = wm; + workbookSettings = ws; + } + + /** + * Copy constructor + */ + DataValiditySettingsRecord(DataValiditySettingsRecord dvsr) + { + super(Type.DV); + + data = dvsr.getData(); + } + + /** + * Constructor + * + * @param dvsr the record copied from a writable sheet + */ + DataValiditySettingsRecord(DataValiditySettingsRecord dvsr, + ExternalSheet es, + WorkbookMethods w, + WorkbookSettings ws) + { + super(Type.DV); + + workbook = w; + externalSheet = es; + workbookSettings = ws; + + Assert.verify(w != null); + Assert.verify(es != null); + + data = new byte[dvsr.data.length]; + System.arraycopy(dvsr.data, 0, data, 0, data.length); + } + + /** + * Constructor called when the API creates a writable data validation + * + * @param dvsr the record copied from a writable sheet + */ + public DataValiditySettingsRecord(DVParser dvp) + { + super(Type.DV); + dvParser = dvp; + } + + /** + * Initializes the dvParser + */ + private void initialize() + { + try + { + if (dvParser == null) + { + dvParser = new DVParser(data, externalSheet, + workbook, workbookSettings); + } + } + catch (FormulaException e) + { + logger.warn("Cannot read drop down range " + e.getMessage()); + } + } + + /** + * Retrieves the data for output to binary file + * + * @return the data to be written + */ + public byte[] getData() + { + if (dvParser == null) + { + return data; + } + + return dvParser.getData(); + } + + /** + * Inserts a row + * + * @param row the row to insert + */ + public void insertRow(int row) + { + if (dvParser == null) + { + initialize(); + } + + dvParser.insertRow(row); + } + + /** + * Removes a row + * + * @param row the row to insert + */ + public void removeRow(int row) + { + if (dvParser == null) + { + initialize(); + } + + dvParser.removeRow(row); + } + + /** + * Inserts a row + * + * @param col the row to insert + */ + public void insertColumn(int col) + { + if (dvParser == null) + { + initialize(); + } + + dvParser.insertColumn(col); + } + + /** + * Removes a column + * + * @param col the row to insert + */ + public void removeColumn(int col) + { + if (dvParser == null) + { + initialize(); + } + + dvParser.removeColumn(col); + } + + /** + * Accessor for first column + * + * @return the first column + */ + public int getFirstColumn() + { + if (dvParser == null) + { + initialize(); + } + + return dvParser.getFirstColumn(); + } + + /** + * Accessor for the last column + * + * @return the last column + */ + public int getLastColumn() + { + if (dvParser == null) + { + initialize(); + } + + return dvParser.getLastColumn(); + } + + /** + * Accessor for first row + * + * @return the first row + */ + public int getFirstRow() + { + if (dvParser == null) + { + initialize(); + } + + return dvParser.getFirstRow(); + } + + /** + * Accessor for the last row + * + * @return the last row + */ + public int getLastRow() + { + if (dvParser == null) + { + initialize(); + } + + return dvParser.getLastRow(); + } + + /** + * Sets the handle to the data validation record + * + * @param dv the data validation + */ + void setDataValidation(DataValidation dv) + { + dataValidation = dv; + } + + /** + * Gets the DVParser. This is used when doing a deep copy of cells + * on the writable side of things + */ + DVParser getDVParser() + { + return dvParser; + } + + public String getValidationFormula() + { + try + { + if (dvParser == null) + { + initialize(); + } + + return dvParser.getValidationFormula(); + } + catch (FormulaException e) + { + logger.warn("Cannot read drop down range " + e.getMessage()); + return ""; + } + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/DisplayFormat.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/DisplayFormat.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/DisplayFormat.java 17 Aug 2012 14:51:14 -0000 1.1 @@ -0,0 +1,55 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +/** + * The interface implemented by the various number and date format styles. + * The methods on this interface are called internally when generating a + * spreadsheet + */ +public interface DisplayFormat +{ + /** + * Accessor for the index style of this format + * + * @return the index for this format + */ + public int getFormatIndex(); + /** + * Accessor to see whether this format has been initialized + * + * @return TRUE if initialized, FALSE otherwise + */ + public boolean isInitialized(); + + /** + * Initializes this format with the specified index number + * + * @param pos the position of this format record in the workbook + */ + public void initialize(int pos); + + /** + * Accessor to determine whether or not this format is built in + * + * @return TRUE if this format is a built in format, FALSE otherwise + */ + public boolean isBuiltIn(); +} Index: 3rdParty_sources/jexcelapi/jxl/biff/DoubleHelper.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/DoubleHelper.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/DoubleHelper.java 17 Aug 2012 14:51:13 -0000 1.1 @@ -0,0 +1,89 @@ +/********************************************************************* +* +* Copyright (C) 2001 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +/** + * Class to help handle doubles + */ +public class DoubleHelper +{ + /** + * Private constructor to prevent instantiation + */ + private DoubleHelper() + { + } + + /** + * Gets the IEEE value from the byte array passed in + * + * @param pos the position in the data block which contains the double value + * @param data the data block containing the raw bytes + * @return the double value converted from the raw data + */ + public static double getIEEEDouble(byte[] data, int pos) + { + int num1 = IntegerHelper.getInt(data[pos], data[pos + 1], + data[pos + 2], data[pos + 3]); + int num2 = IntegerHelper.getInt(data[pos + 4], data[pos + 5], + data[pos + 6], data[pos + 7]); + + // Long.parseLong doesn't like the sign bit, so have to extract this + // information and put it in at the end. (Acknowledgment: thanks + // to Ruben for pointing this out) + boolean negative = ((num2 & 0x80000000) != 0); + + // Thanks to Lyle for the following improved IEEE double processing + long val = ((num2 & 0x7fffffff) * 0x100000000L) + + (num1 < 0 ? 0x100000000L + num1 : num1); + double value = Double.longBitsToDouble(val); + + if (negative) + { + value = -value; + } + return value; + } + + /** + * Puts the IEEE representation of the double provided into the array + * at the designated position + * + * @param target the data block into which the binary representation is to + * be placed + * @param pos the position in target in which to place the bytes + * @param d the double value to convert to raw bytes + */ + public static void getIEEEBytes(double d, byte[] target, int pos) + { + long val = Double.doubleToLongBits(d); + target[pos] = (byte) (val & 0xff); + target[pos + 1] = (byte) ((val & 0xff00) >> 8); + target[pos + 2] = (byte) ((val & 0xff0000) >> 16); + target[pos + 3] = (byte) ((val & 0xff000000) >> 24); + target[pos + 4] = (byte) ((val & 0xff00000000L) >> 32); + target[pos + 5] = (byte) ((val & 0xff0000000000L) >> 40); + target[pos + 6] = (byte) ((val & 0xff000000000000L) >> 48); + target[pos + 7] = (byte) ((val & 0xff00000000000000L) >> 56) ; + } +} + + + Index: 3rdParty_sources/jexcelapi/jxl/biff/EmptyCell.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/EmptyCell.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/EmptyCell.java 17 Aug 2012 14:51:13 -0000 1.1 @@ -0,0 +1,219 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +import jxl.CellFeatures; +import jxl.CellType; +import jxl.format.Alignment; +import jxl.format.CellFormat; +import jxl.format.Border; +import jxl.format.BorderLineStyle; +import jxl.format.VerticalAlignment; +import jxl.write.WritableCell; +import jxl.write.WritableCellFeatures; + +/** + * An empty cell. Represents an empty, as opposed to a blank cell + * in the workbook + */ +public class EmptyCell implements WritableCell +{ + /** + * The row of this empty cell + */ + private int row; + /** + * The column number of this empty cell + */ + private int col; + + /** + * Constructs an empty cell at the specified position + * + * @param c the zero based column + * @param r the zero based row + */ + public EmptyCell(int c, int r) + { + row = r; + col = c; + } + + /** + * Returns the row number of this cell + * + * @return the row number of this cell + */ + public int getRow() + { + return row; + } + + /** + * Returns the column number of this cell + * + * @return the column number of this cell + */ + public int getColumn() + { + return col; + } + + /** + * Returns the content type of this cell + * + * @return the content type for this cell + */ + public CellType getType() + { + return CellType.EMPTY; + } + + /** + * Quick and dirty function to return the contents of this cell as a string. + * + * @return an empty string + */ + public String getContents() + { + return ""; + } + + /** + * Accessor for the format which is applied to this cell + * + * @return the format applied to this cell + */ + public CellFormat getCellFormat() + { + return null; + } + + /** + * Dummy override + * @param flag the hidden flag + */ + public void setHidden(boolean flag) + { + } + + /** + * Dummy override + * @param flag dummy + */ + public void setLocked(boolean flag) + { + } + + /** + * Dummy override + * @param align dummy + */ + public void setAlignment(Alignment align) + { + } + + /** + * Dummy override + * @param valign dummy + */ + public void setVerticalAlignment(VerticalAlignment valign) + { + } + + /** + * Dummy override + * @param line dummy + * @param border dummy + */ + public void setBorder(Border border, BorderLineStyle line) + { + } + + /** + * Dummy override + * @param cf dummy + */ + public void setCellFormat(CellFormat cf) + { + } + + /** + * Dummy override + * @param cf dummy + * @deprecated + */ + public void setCellFormat(jxl.CellFormat cf) + { + } + + /** + * Indicates whether or not this cell is hidden, by virtue of either + * the entire row or column being collapsed + * + * @return TRUE if this cell is hidden, FALSE otherwise + */ + public boolean isHidden() + { + return false; + } + + /** + * Implementation of the deep copy function + * + * @param c the column which the new cell will occupy + * @param r the row which the new cell will occupy + * @return a copy of this cell, which can then be added to the sheet + */ + public WritableCell copyTo(int c, int r) + { + return new EmptyCell(c, r); + } + + /** + * Accessor for the cell features + * + * @return the cell features or NULL if this cell doesn't have any + */ + public CellFeatures getCellFeatures() + { + return null; + } + + /** + * Accessor for the cell features + * + * @return the cell features or NULL if this cell doesn't have any + */ + public WritableCellFeatures getWritableCellFeatures() + { + return null; + } + + /** + * Accessor for the cell features + */ + public void setCellFeatures(WritableCellFeatures wcf) + { + } + +} + + Index: 3rdParty_sources/jexcelapi/jxl/biff/EncodedURLHelper.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/EncodedURLHelper.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/EncodedURLHelper.java 17 Aug 2012 14:51:13 -0000 1.1 @@ -0,0 +1,142 @@ +/********************************************************************* +* +* Copyright (C) 2005 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +import common.Logger; + +import jxl.WorkbookSettings; + +/** + * Helper to get the Microsoft encoded URL from the given string + */ +public class EncodedURLHelper +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(EncodedURLHelper.class); + + // The control codes + private static byte msDosDriveLetter = 0x01; + private static byte sameDrive = 0x02; + private static byte endOfSubdirectory = 0x03; + private static byte parentDirectory = 0x04; + private static byte unencodedUrl = 0x05; + + public static byte[] getEncodedURL(String s, WorkbookSettings ws) + { + if (s.startsWith("http:")) + { + return getURL(s, ws); + } + else + { + return getFile(s, ws); + } + } + + private static byte[] getFile(String s, WorkbookSettings ws) + { + ByteArray byteArray = new ByteArray(); + + int pos = 0; + if (s.charAt(1) == ':') + { + // we have a drive letter + byteArray.add(msDosDriveLetter); + byteArray.add((byte) s.charAt(0)); + pos = 2; + } + else if (s.charAt(pos) == '\\' || + s.charAt(pos) == '/') + { + byteArray.add(sameDrive); + } + + while (s.charAt(pos) == '\\' || + s.charAt(pos) == '/') + { + pos++; + } + + while (pos < s.length()) + { + int nextSepIndex1 = s.indexOf('/', pos); + int nextSepIndex2 = s.indexOf('\\', pos); + int nextSepIndex = 0; + String nextFileNameComponent = null; + + if (nextSepIndex1 != -1 && nextSepIndex2 != -1) + { + // choose the smallest (ie. nearest) separator + nextSepIndex = Math.min(nextSepIndex1, nextSepIndex2); + } + else if (nextSepIndex1 == -1 || nextSepIndex2 == -1) + { + // chose the maximum separator + nextSepIndex = Math.max(nextSepIndex1, nextSepIndex2); + } + + if (nextSepIndex == -1) + { + // no more separators + nextFileNameComponent = s.substring(pos); + pos = s.length(); + } + else + { + nextFileNameComponent = s.substring(pos, nextSepIndex); + pos = nextSepIndex + 1; + } + + if (nextFileNameComponent.equals(".")) + { + // current directory - do nothing + } + else if (nextFileNameComponent.equals("..")) + { + // parent directory + byteArray.add(parentDirectory); + } + else + { + // add the filename component + byteArray.add(StringHelper.getBytes(nextFileNameComponent, + ws)); + } + + if (pos < s.length()) + { + byteArray.add(endOfSubdirectory); + } + } + + return byteArray.getBytes(); + } + + private static byte[] getURL(String s, WorkbookSettings ws) + { + ByteArray byteArray = new ByteArray(); + byteArray.add(unencodedUrl); + byteArray.add((byte) s.length()); + byteArray.add(StringHelper.getBytes(s, ws)); + return byteArray.getBytes(); + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/FilterModeRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/FilterModeRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/FilterModeRecord.java 17 Aug 2012 14:51:13 -0000 1.1 @@ -0,0 +1,60 @@ +/********************************************************************* +* +* Copyright (C) 2007 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +import common.Logger; + +import jxl.read.biff.Record; + +/** + * Range information for conditional formatting + */ +public class FilterModeRecord extends WritableRecordData +{ + // The logger + private static Logger logger = Logger.getLogger(FilterModeRecord.class); + + /** + * The data + */ + private byte[] data; + + + /** + * Constructor + */ + public FilterModeRecord(Record t) + { + super(t); + + data = getRecord().getData(); + } + + /** + * Retrieves the data for output to binary file + * + * @return the data to be written + */ + public byte[] getData() + { + return data; + } +} + Index: 3rdParty_sources/jexcelapi/jxl/biff/FontRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/FontRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/FontRecord.java 17 Aug 2012 14:51:13 -0000 1.1 @@ -0,0 +1,547 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +import common.Assert; +import common.Logger; + +import jxl.WorkbookSettings; +import jxl.format.Colour; +import jxl.format.Font; +import jxl.format.ScriptStyle; +import jxl.format.UnderlineStyle; +import jxl.read.biff.Record; + +/** + * A record containing the necessary data for the font information + */ +public class FontRecord extends WritableRecordData implements Font +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(FontRecord.class); + + /** + * The point height of this font + */ + private int pointHeight; + /** + * The index into the colour palette + */ + private int colourIndex; + /** + * The bold weight for this font (normal or bold) + */ + private int boldWeight; + /** + * The style of the script (italic or normal) + */ + private int scriptStyle; + /** + * The underline style for this font (none, single, double etc) + */ + private int underlineStyle; + /** + * The font family + */ + private byte fontFamily; + /** + * The character set + */ + private byte characterSet; + + /** + * Indicates whether or not this font is italic + */ + private boolean italic; + /** + * Indicates whether or not this font is struck out + */ + private boolean struckout; + /** + * The name of this font + */ + private String name; + /** + * Flag to indicate whether the derived data (such as the font index) has + * been initialized or not + */ + private boolean initialized; + + /** + * The index of this font in the font list + */ + private int fontIndex; + + /** + * Dummy indicators for overloading the constructor + */ + private static class Biff7 {}; + public static final Biff7 biff7 = new Biff7(); + + /** + * The conversion factor between microsoft internal units and point size + */ + private static final int EXCEL_UNITS_PER_POINT = 20; + + /** + * Constructor, used when creating a new font for writing out. + * + * @param bold the bold indicator + * @param ps the point size + * @param us the underline style + * @param fn the name + * @param it italicised indicator + * @param ss the script style + * @param ci the colour index + */ + protected FontRecord(String fn, int ps, int bold, boolean it, + int us, int ci, int ss) + { + super(Type.FONT); + boldWeight = bold; + underlineStyle = us; + name = fn; + pointHeight = ps; + italic = it; + scriptStyle = ss; + colourIndex = ci; + initialized = false; + struckout = false; + } + + /** + * Constructs this object from the raw data. Used when reading in a + * format record + * + * @param t the raw data + * @param ws the workbook settings + */ + public FontRecord(Record t, WorkbookSettings ws) + { + super(t); + + byte[] data = getRecord().getData(); + + pointHeight = IntegerHelper.getInt(data[0], data[1]) / + EXCEL_UNITS_PER_POINT; + colourIndex = IntegerHelper.getInt(data[4], data[5]); + boldWeight = IntegerHelper.getInt(data[6], data[7]); + scriptStyle = IntegerHelper.getInt(data[8], data[9]); + underlineStyle = data[10]; + fontFamily = data[11]; + characterSet = data[12]; + initialized = false; + + if ((data[2] & 0x02) != 0) + { + italic = true; + } + + if ((data[2] & 0x08) != 0) + { + struckout = true; + } + + int numChars = data[14]; + if (data[15] == 0) + { + name = StringHelper.getString(data, numChars, 16, ws); + } + else if (data[15] == 1) + { + name = StringHelper.getUnicodeString(data, numChars, 16); + } + else + { + // Some font names don't have the unicode indicator + name = StringHelper.getString(data, numChars, 15, ws); + } + } + + /** + * Constructs this object from the raw data. Used when reading in a + * format record + * + * @param t the raw data + * @param ws the workbook settings + * @param dummy dummy overload + */ + public FontRecord(Record t, WorkbookSettings ws, Biff7 dummy) + { + super(t); + + byte[] data = getRecord().getData(); + + pointHeight = IntegerHelper.getInt(data[0], data[1]) / + EXCEL_UNITS_PER_POINT; + colourIndex = IntegerHelper.getInt(data[4], data[5]); + boldWeight = IntegerHelper.getInt(data[6], data[7]); + scriptStyle = IntegerHelper.getInt(data[8], data[9]); + underlineStyle = data[10]; + fontFamily = data[11]; + initialized = false; + + if ((data[2] & 0x02) != 0) + { + italic = true; + } + + if ((data[2] & 0x08) != 0) + { + struckout = true; + } + + int numChars = data[14]; + name = StringHelper.getString(data, numChars, 15, ws); + } + + /** + * Publicly available copy constructor + * + * @param f the font to copy + */ + protected FontRecord(Font f) + { + super(Type.FONT); + + Assert.verify(f != null); + + pointHeight = f.getPointSize(); + colourIndex = f.getColour().getValue(); + boldWeight = f.getBoldWeight(); + scriptStyle = f.getScriptStyle().getValue(); + underlineStyle = f.getUnderlineStyle().getValue(); + italic = f.isItalic(); + name = f.getName(); + struckout = f.isStruckout(); + initialized = false; + } + + /** + * Gets the byte data for writing out + * + * @return the raw data + */ + public byte[] getData() + { + byte[] data = new byte[16 + name.length() * 2]; + + // Excel expects font heights in 1/20ths of a point + IntegerHelper.getTwoBytes(pointHeight * EXCEL_UNITS_PER_POINT, data, 0); + + // Set the font attributes to be zero for now + if (italic) + { + data[2] |= 0x2; + } + + if (struckout) + { + data[2] |= 0x08; + } + + // Set the index to the colour palette + IntegerHelper.getTwoBytes(colourIndex, data, 4); + + // Bold style + IntegerHelper.getTwoBytes(boldWeight, data, 6); + + // Script style + IntegerHelper.getTwoBytes(scriptStyle, data, 8); + + // Underline style + data[10] = (byte) underlineStyle; + + // Set the font family to be 0 + data[11] = fontFamily; + + // Set the character set to be zero + data[12] = characterSet; + + // Set the reserved bit to be zero + data[13] = 0; + + // Set the length of the font name + data[14] = (byte) name.length(); + + data[15] = (byte) 1; + + // Copy in the string + StringHelper.getUnicodeBytes(name, data, 16); + + return data; + } + + /** + * Accessor to see whether this object is initialized or not. + * + * @return TRUE if this font record has been initialized, FALSE otherwise + */ + public final boolean isInitialized() + { + return initialized; + } + + /** + * Sets the font index of this record. Called from the FormattingRecords + * object + * + * @param pos the position of this font in the workbooks font list + */ + public final void initialize(int pos) + { + fontIndex = pos; + initialized = true; + } + + /** + * Resets the initialize flag. This is called by the constructor of + * WritableWorkbookImpl to reset the statically declared fonts + */ + public final void uninitialize() + { + initialized = false; + } + + /** + * Accessor for the font index + * + * @return the font index + */ + public final int getFontIndex() + { + return fontIndex; + } + + /** + * Sets the point size for this font, if the font hasn't been initialized + * + * @param ps the point size + */ + protected void setFontPointSize(int ps) + { + Assert.verify(!initialized); + + pointHeight = ps; + } + + /** + * Gets the point size for this font, if the font hasn't been initialized + * + * @return the point size + */ + public int getPointSize() + { + return pointHeight; + } + + /** + * Sets the bold style for this font, if the font hasn't been initialized + * + * @param bs the bold style + */ + protected void setFontBoldStyle(int bs) + { + Assert.verify(!initialized); + + boldWeight = bs; + } + + /** + * Gets the bold weight for this font + * + * @return the bold weight for this font + */ + public int getBoldWeight() + { + return boldWeight; + } + + /** + * Sets the italic indicator for this font, if the font hasn't been + * initialized + * + * @param i the italic flag + */ + protected void setFontItalic(boolean i) + { + Assert.verify(!initialized); + + italic = i; + } + + /** + * Returns the italic flag + * + * @return TRUE if this font is italic, FALSE otherwise + */ + public boolean isItalic() + { + return italic; + } + + /** + * Sets the underline style for this font, if the font hasn't been + * initialized + * + * @param us the underline style + */ + protected void setFontUnderlineStyle(int us) + { + Assert.verify(!initialized); + + underlineStyle = us; + } + + /** + * Gets the underline style for this font + * + * @return the underline style + */ + public UnderlineStyle getUnderlineStyle() + { + return UnderlineStyle.getStyle(underlineStyle); + } + + /** + * Sets the colour for this font, if the font hasn't been + * initialized + * + * @param c the colour + */ + protected void setFontColour(int c) + { + Assert.verify(!initialized); + + colourIndex = c; + } + + /** + * Gets the colour for this font + * + * @return the colour + */ + public Colour getColour() + { + return Colour.getInternalColour(colourIndex); + } + + /** + * Sets the script style (eg. superscript, subscript) for this font, + * if the font hasn't been initialized + * + * @param ss the colour + */ + protected void setFontScriptStyle(int ss) + { + Assert.verify(!initialized); + + scriptStyle = ss; + } + + /** + * Gets the script style + * + * @return the script style + */ + public ScriptStyle getScriptStyle() + { + return ScriptStyle.getStyle(scriptStyle); + } + + /** + * Gets the name of this font + * + * @return the name of this font + */ + public String getName() + { + return name; + } + + /** + * Standard hash code method + * @return the hash code for this object + */ + public int hashCode() + { + return name.hashCode(); + } + + /** + * Standard equals method + * @param o the object to compare + * @return TRUE if the objects are equal, FALSE otherwise + */ + public boolean equals(Object o) + { + if (o == this) + { + return true; + } + + if (!(o instanceof FontRecord)) + { + return false; + } + + FontRecord font = (FontRecord) o; + + if (pointHeight == font.pointHeight && + colourIndex == font.colourIndex && + boldWeight == font.boldWeight && + scriptStyle == font.scriptStyle && + underlineStyle == font.underlineStyle && + italic == font.italic && + struckout == font.struckout && + fontFamily == font.fontFamily && + characterSet == font.characterSet && + name.equals(font.name)) + { + return true; + } + + return false; + } + + /** + * Accessor for the strike out flag + * + * @return TRUE if this font is struck out, FALSE otherwise + */ + public boolean isStruckout() + { + return struckout; + } + + /** + * Sets the struck out flag + * + * @param os TRUE if the font is struck out, false otherwise + */ + protected void setFontStruckout(boolean os) + { + struckout = os; + } +} + + Index: 3rdParty_sources/jexcelapi/jxl/biff/Fonts.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/Fonts.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/Fonts.java 17 Aug 2012 14:51:14 -0000 1.1 @@ -0,0 +1,181 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; + +import common.Assert; + +import jxl.write.biff.File; + +/** + * A container for the list of fonts used in this workbook + */ +public class Fonts +{ + /** + * The list of fonts + */ + private ArrayList fonts; + + /** + * The default number of fonts + */ + private static final int numDefaultFonts = 4; + + /** + * Constructor + */ + public Fonts() + { + fonts = new ArrayList(); + } + + /** + * Adds a font record to this workbook. If the FontRecord passed in has not + * been initialized, then its font index is determined based upon the size + * of the fonts list. The FontRecord's initialized method is called, and + * it is added to the list of fonts. + * + * @param f the font to add + */ + public void addFont(FontRecord f) + { + if (!f.isInitialized()) + { + int pos = fonts.size(); + + // Remember that the pos with index 4 is skipped + if (pos >= 4) + { + pos++; + } + + f.initialize(pos); + fonts.add(f); + } + } + + /** + * Used by FormattingRecord for retrieving the fonts for the + * hardcoded styles + * + * @param index the index of the font to return + * @return the font with the specified font index + */ + public FontRecord getFont(int index) + { + // remember to allow for the fact that font index 4 is not used + if (index > 4) + { + index--; + } + + return (FontRecord) fonts.get(index); + } + + /** + * Writes out the list of fonts + * + * @param outputFile the compound file to write the data to + * @exception IOException + */ + public void write(File outputFile) throws IOException + { + Iterator i = fonts.iterator(); + + FontRecord font = null; + while (i.hasNext()) + { + font = (FontRecord) i.next(); + outputFile.write(font); + } + } + + /** + * Rationalizes all the fonts, removing any duplicates + * + * @return the mappings between new indexes and old ones + */ + IndexMapping rationalize() + { + IndexMapping mapping = new IndexMapping(fonts.size() + 1); + // allow for skipping record 4 + + ArrayList newfonts = new ArrayList(); + FontRecord fr = null; + int numremoved = 0; + + // Preserve the default fonts + for (int i = 0; i < numDefaultFonts; i++) + { + fr = (FontRecord) fonts.get(i); + newfonts.add(fr); + mapping.setMapping(fr.getFontIndex(), fr.getFontIndex()); + } + + // Now do the rest + Iterator it = null; + FontRecord fr2 = null; + boolean duplicate = false; + for (int i = numDefaultFonts; i < fonts.size(); i++) + { + fr = (FontRecord) fonts.get(i); + + // Compare to all the fonts currently on the list + duplicate = false; + it = newfonts.iterator(); + while (it.hasNext() && !duplicate) + { + fr2 = (FontRecord) it.next(); + if (fr.equals(fr2)) + { + duplicate = true; + mapping.setMapping(fr.getFontIndex(), + mapping.getNewIndex(fr2.getFontIndex())); + numremoved++; + } + } + + if (!duplicate) + { + // Add to the new list + newfonts.add(fr); + int newindex = fr.getFontIndex() - numremoved; + Assert.verify(newindex > 4); + mapping.setMapping(fr.getFontIndex(), newindex); + } + } + + // Iterate through the remaining fonts, updating all the font indices + it = newfonts.iterator(); + while (it.hasNext()) + { + fr = (FontRecord) it.next(); + fr.initialize(mapping.getNewIndex(fr.getFontIndex())); + } + + fonts = newfonts; + + return mapping; + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/FormatRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/FormatRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/FormatRecord.java 17 Aug 2012 14:51:14 -0000 1.1 @@ -0,0 +1,642 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +import java.text.DateFormat; +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.text.SimpleDateFormat; + +import common.Logger; + +import jxl.WorkbookSettings; +import jxl.format.Format; +import jxl.read.biff.Record; + +/** + * A non-built in format record + */ +public class FormatRecord extends WritableRecordData + implements DisplayFormat, Format +{ + /** + * The logger + */ + public static Logger logger = Logger.getLogger(FormatRecord.class); + + /** + * Initialized flag + */ + private boolean initialized; + + /** + * The raw data + */ + private byte[] data; + + /** + * The index code + */ + private int indexCode; + + /** + * The formatting string + */ + private String formatString; + + /** + * Indicates whether this is a date formatting record + */ + private boolean date; + + /** + * Indicates whether this a number formatting record + */ + private boolean number; + + /** + * The format object + */ + private java.text.Format format; + + /** + * The date strings to look for + */ + private static String[] dateStrings = new String[] + { + "dd", + "mm", + "yy", + "hh", + "ss", + "m/", + "/d" + }; + + // Type to distinguish between biff7 and biff8 + private static class BiffType + { + } + + public static final BiffType biff8 = new BiffType(); + public static final BiffType biff7 = new BiffType(); + + /** + * Constructor invoked when copying sheets + * + * @param fmt the format string + * @param refno the index code + */ + FormatRecord(String fmt, int refno) + { + super(Type.FORMAT); + formatString = fmt; + indexCode = refno; + initialized = true; + } + + /** + * Constructor used by writable formats + */ + protected FormatRecord() + { + super(Type.FORMAT); + initialized = false; + } + + /** + * Copy constructor - can be invoked by public access + * + * @param fr the format to copy + */ + protected FormatRecord(FormatRecord fr) + { + super(Type.FORMAT); + initialized = false; + + formatString = fr.formatString; + date = fr.date; + number = fr.number; + // format = (java.text.Format) fr.format.clone(); + } + + /** + * Constructs this object from the raw data. Used when reading in a + * format record + * + * @param t the raw data + * @param ws the workbook settings + * @param biffType biff type dummy overload + */ + public FormatRecord(Record t, WorkbookSettings ws, BiffType biffType) + { + super(t); + + byte[] data = getRecord().getData(); + indexCode = IntegerHelper.getInt(data[0], data[1]); + initialized = true; + + if (biffType == biff8) + { + int numchars = IntegerHelper.getInt(data[2], data[3]); + if (data[4] == 0) + { + formatString = StringHelper.getString(data, numchars, 5, ws); + } + else + { + formatString = StringHelper.getUnicodeString(data, numchars, 5); + } + } + else + { + int numchars = data[2]; + byte[] chars = new byte[numchars]; + System.arraycopy(data, 3, chars, 0, chars.length); + formatString = new String(chars); + } + + date = false; + number = false; + + // First see if this is a date format + for (int i = 0 ; i < dateStrings.length; i++) + { + String dateString = dateStrings[i]; + if (formatString.indexOf(dateString) != -1 || + formatString.indexOf(dateString.toUpperCase()) != -1) + { + date = true; + break; + } + } + + // See if this is number format - look for the # or 0 characters + if (!date) + { + if (formatString.indexOf('#') != -1 || + formatString.indexOf('0') != -1 ) + { + number = true; + } + } + } + + /** + * Used to get the data when writing out the format record + * + * @return the raw data + */ + public byte[] getData() + { + data = new byte[formatString.length() * 2 + 3 + 2]; + + IntegerHelper.getTwoBytes(indexCode, data, 0); + IntegerHelper.getTwoBytes(formatString.length(), data, 2); + data[4] = (byte) 1; // unicode indicator + StringHelper.getUnicodeBytes(formatString, data, 5); + + return data; + } + + /** + * Gets the format index of this record + * + * @return the format index of this record + */ + public int getFormatIndex() + { + return indexCode; + } + + /** + * Accessor to see whether this object is initialized or not. + * + * @return TRUE if this font record has been initialized, FALSE otherwise + */ + public boolean isInitialized() + { + return initialized; + } + + /** + * Sets the index of this record. Called from the FormattingRecords + * object + * + * @param pos the position of this font in the workbooks font list + */ + + public void initialize(int pos) + { + indexCode = pos; + initialized = true; + } + + /** + * Replaces all instances of search with replace in the input. Used for + * replacing microsoft number formatting characters with java equivalents + * + * @param input the format string + * @param search the Excel character to be replaced + * @param replace the java equivalent + * @return the input string with the specified substring replaced + */ + protected final String replace(String input, String search, String replace) + { + String fmtstr = input; + int pos = fmtstr.indexOf(search); + while (pos != -1) + { + StringBuffer tmp = new StringBuffer(fmtstr.substring(0, pos)); + tmp.append(replace); + tmp.append(fmtstr.substring(pos + search.length())); + fmtstr = tmp.toString(); + pos = fmtstr.indexOf(search); + } + return fmtstr; + } + + /** + * Called by the immediate subclass to set the string + * once the Java-Excel replacements have been done + * + * @param s the format string + */ + protected final void setFormatString(String s) + { + formatString = s; + } + + /** + * Sees if this format is a date format + * + * @return TRUE if this format is a date + */ + public final boolean isDate() + { + return date; + } + + /** + * Sees if this format is a number format + * + * @return TRUE if this format is a number + */ + public final boolean isNumber() + { + return number; + } + + /** + * Gets the java equivalent number format for the formatString + * + * @return The java equivalent of the number format for this object + */ + public final NumberFormat getNumberFormat() + { + if (format != null && format instanceof NumberFormat) + { + return (NumberFormat) format; + } + + try + { + String fs = formatString; + + // Replace the Excel formatting characters with java equivalents + fs = replace(fs, "E+", "E"); + fs = replace(fs, "_)", ""); + fs = replace(fs, "_", ""); + fs = replace(fs, "[Red]", ""); + fs = replace(fs, "\\", ""); + + format = new DecimalFormat(fs); + } + catch (IllegalArgumentException e) + { + // Something went wrong with the date format - fail silently + // and return a default value + format = new DecimalFormat("#.###"); + } + + return (NumberFormat) format; + } + + /** + * Gets the java equivalent date format for the formatString + * + * @return The java equivalent of the date format for this object + */ + public final DateFormat getDateFormat() + { + if (format != null && format instanceof DateFormat) + { + return (DateFormat) format; + } + + String fmt = formatString; + + // Replace the AM/PM indicator with an a + int pos = fmt.indexOf("AM/PM"); + while (pos != -1) + { + StringBuffer sb = new StringBuffer(fmt.substring(0, pos)); + sb.append('a'); + sb.append(fmt.substring(pos + 5)); + fmt = sb.toString(); + pos = fmt.indexOf("AM/PM"); + } + + // Replace ss.0 with ss.SSS (necessary to always specify milliseconds + // because of NT) + pos = fmt.indexOf("ss.0"); + while (pos != -1) + { + StringBuffer sb = new StringBuffer(fmt.substring(0, pos)); + sb.append("ss.SSS"); + + // Keep going until we run out of zeros + pos += 4; + while (pos < fmt.length() && fmt.charAt(pos) == '0') + { + pos++; + } + + sb.append(fmt.substring(pos)); + fmt = sb.toString(); + pos = fmt.indexOf("ss.0"); + } + + + // Filter out the backslashes + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < fmt.length(); i++) + { + if (fmt.charAt(i) != '\\') + { + sb.append(fmt.charAt(i)); + } + } + + fmt = sb.toString(); + + // If the date format starts with anything inside square brackets then + // filter tham out + if (fmt.charAt(0) == '[') + { + int end = fmt.indexOf(']'); + if (end != -1) + { + fmt = fmt.substring(end+1); + } + } + + // Get rid of some spurious characters that can creep in + fmt = replace(fmt, ";@", ""); + + // We need to convert the month indicator m, to upper case when we + // are dealing with dates + char[] formatBytes = fmt.toCharArray(); + + for (int i = 0; i < formatBytes.length; i++) + { + if (formatBytes[i] == 'm') + { + // Firstly, see if the preceding character is also an m. If so, + // copy that + if (i > 0 && (formatBytes[i - 1] == 'm' || formatBytes[i - 1] == 'M')) + { + formatBytes[i] = formatBytes[i - 1]; + } + else + { + // There is no easy way out. We have to deduce whether this an + // minute or a month? See which is closest out of the + // letters H d s or y + // First, h + int minuteDist = Integer.MAX_VALUE; + for (int j = i - 1; j > 0; j--) + { + if (formatBytes[j] == 'h') + { + minuteDist = i - j; + break; + } + } + + for (int j = i + 1; j < formatBytes.length; j++) + { + if (formatBytes[j] == 'h') + { + minuteDist = Math.min(minuteDist, j - i); + break; + } + } + + for (int j = i - 1; j > 0; j--) + { + if (formatBytes[j] == 'H') + { + minuteDist = i - j; + break; + } + } + + for (int j = i + 1; j < formatBytes.length; j++) + { + if (formatBytes[j] == 'H') + { + minuteDist = Math.min(minuteDist, j - i); + break; + } + } + + // Now repeat for s + for (int j = i - 1; j > 0; j--) + { + if (formatBytes[j] == 's') + { + minuteDist = Math.min(minuteDist, i - j); + break; + } + } + for (int j = i + 1; j < formatBytes.length; j++) + { + if (formatBytes[j] == 's') + { + minuteDist = Math.min(minuteDist, j - i); + break; + } + } + // We now have the distance of the closest character which could + // indicate the the m refers to a minute + // Repeat for d and y + int monthDist = Integer.MAX_VALUE; + for (int j = i - 1; j > 0; j--) + { + if (formatBytes[j] == 'd') + { + monthDist = i - j; + break; + } + } + + for (int j = i + 1; j < formatBytes.length; j++) + { + if (formatBytes[j] == 'd') + { + monthDist = Math.min(monthDist, j - i); + break; + } + } + // Now repeat for y + for (int j = i - 1; j > 0; j--) + { + if (formatBytes[j] == 'y') + { + monthDist = Math.min(monthDist, i - j); + break; + } + } + for (int j = i + 1; j < formatBytes.length; j++) + { + if (formatBytes[j] == 'y') + { + monthDist = Math.min(monthDist, j - i); + break; + } + } + + if (monthDist < minuteDist) + { + // The month indicator is closer, so convert to a capital M + formatBytes[i] = Character.toUpperCase(formatBytes[i]); + } + else if ((monthDist == minuteDist) && + (monthDist != Integer.MAX_VALUE)) + { + // They are equidistant. As a tie-breaker, take the formatting + // character which precedes the m + char ind = formatBytes[i - monthDist]; + if (ind == 'y' || ind == 'd') + { + // The preceding item indicates a month measure, so convert + formatBytes[i] = Character.toUpperCase(formatBytes[i]); + } + } + } + } + } + + try + { + this.format = new SimpleDateFormat(new String(formatBytes)); + } + catch (IllegalArgumentException e) + { + // There was a spurious character - fail silently + this.format = new SimpleDateFormat("dd MM yyyy hh:mm:ss"); + } + return (DateFormat) this.format; + } + + /** + * Gets the index code, for use as a hash value + * + * @return the ifmt code for this cell + */ + public int getIndexCode() + { + return indexCode; + } + + /** + * Gets the formatting string. + * + * @return the excel format string + */ + public String getFormatString() + { + return formatString; + } + + /** + * Indicates whether this formula is a built in + * + * @return FALSE + */ + public boolean isBuiltIn() + { + return false; + } + + /** + * Standard hash code method + * @return the hash code value for this object + */ + public int hashCode() + { + return formatString.hashCode(); + } + + /** + * Standard equals method. This compares the contents of two + * format records, and not their indexCodes, which are ignored + * + * @param o the object to compare + * @return TRUE if the two objects are equal, FALSE otherwise + */ + public boolean equals(Object o) + { + if (o == this) + { + return true; + } + + if (!(o instanceof FormatRecord)) + { + return false; + } + + FormatRecord fr = (FormatRecord) o; + + // Initialized format comparison + if (initialized && fr.initialized) + { + // Must be either a number or a date format + if (date != fr.date || + number != fr.number) + { + return false; + } + + return formatString.equals(fr.formatString); + } + + // Uninitialized format comparison + return formatString.equals(fr.formatString); + } +} + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/biff/FormattingRecords.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/FormattingRecords.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/FormattingRecords.java 17 Aug 2012 14:51:14 -0000 1.1 @@ -0,0 +1,573 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +import java.io.IOException; +import java.text.DateFormat; +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; + +import common.Assert; +import common.Logger; + +import jxl.format.Colour; +import jxl.format.RGB; +import jxl.write.biff.File; + +/** + * The list of XF records and formatting records for the workbook + */ +public class FormattingRecords +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(FormattingRecords.class); + + /** + * A hash map of FormatRecords, for random access retrieval when reading + * in a spreadsheet + */ + private HashMap formats; + + /** + * A list of formats, used when writing out a spreadsheet + */ + private ArrayList formatsList; + + /** + * The list of extended format records + */ + private ArrayList xfRecords; + + /** + * The next available index number for custom format records + */ + private int nextCustomIndexNumber; + + /** + * A handle to the available fonts + */ + private Fonts fonts; + + /** + * The colour palette + */ + private PaletteRecord palette; + + /** + * The start index number for custom format records + */ + private static final int customFormatStartIndex = 0xa4; + + /** + * The maximum number of format records. This is some weird internal + * Excel constraint + */ + private static final int maxFormatRecordsIndex = 0x1b9; + + /** + * The minimum number of XF records for a sheet. The rationalization + * processes commences immediately after this number + */ + private static final int minXFRecords = 21; + + /** + * Constructor + * + * @param f the container for the fonts + */ + public FormattingRecords(Fonts f) + { + xfRecords = new ArrayList(10); + formats = new HashMap(10); + formatsList = new ArrayList(10); + fonts = f; + nextCustomIndexNumber = customFormatStartIndex; + } + + /** + * Adds an extended formatting record to the list. If the XF record passed + * in has not been initialized, its index is determined based on the + * xfRecords list, and + * this position is passed to the XF records initialize method + * + * @param xf the xf record to add + * @exception NumFormatRecordsException + */ + public final void addStyle(XFRecord xf) + throws NumFormatRecordsException + { + if (!xf.isInitialized()) + { + int pos = xfRecords.size(); + xf.initialize(pos, this, fonts); + xfRecords.add(xf); + } + else + { + // The XF record has probably been read in. If the index is greater + // Than the size of the list, then it is not a preset format, + // so add it + if (xf.getXFIndex() >= xfRecords.size()) + { + xfRecords.add(xf); + } + } + } + + /** + * Adds a cell format to the hash map, keyed on its index. If the format + * record is not initialized, then its index number is determined and its + * initialize method called. If the font is not a built in format, then it + * is added to the list of formats for writing out + * + * @param fr the format record + */ + public final void addFormat(DisplayFormat fr) + throws NumFormatRecordsException + { + // Handle the case the where the index number in the read Excel + // file exhibits some major weirdness + if (fr.isInitialized() && + fr.getFormatIndex() >= maxFormatRecordsIndex) + { + logger.warn("Format index exceeds Excel maximum - assigning custom " + + "number"); + fr.initialize(nextCustomIndexNumber); + nextCustomIndexNumber++; + } + + // Initialize the format record with a custom index number + if (!fr.isInitialized()) + { + fr.initialize(nextCustomIndexNumber); + nextCustomIndexNumber++; + } + + if (nextCustomIndexNumber > maxFormatRecordsIndex) + { + nextCustomIndexNumber = maxFormatRecordsIndex; + throw new NumFormatRecordsException(); + } + + if (fr.getFormatIndex() >= nextCustomIndexNumber) + { + nextCustomIndexNumber = fr.getFormatIndex() + 1; + } + + if (!fr.isBuiltIn()) + { + formatsList.add(fr); + formats.put(new Integer(fr.getFormatIndex()), fr); + } + } + + /** + * Sees if the extended formatting record at the specified position + * represents a date. First checks against the built in formats, and + * then checks against the hash map of FormatRecords + * + * @param pos the xf format index + * @return TRUE if this format index is formatted as a Date + */ + public final boolean isDate(int pos) + { + XFRecord xfr = (XFRecord) xfRecords.get(pos); + + if (xfr.isDate()) + { + return true; + } + + FormatRecord fr = (FormatRecord) + formats.get(new Integer(xfr.getFormatRecord())); + + return fr == null ? false : fr.isDate(); + } + + /** + * Gets the DateFormat used to format the cell. + * + * @param pos the xf format index + * @return the DateFormat object used to format the date in the original + * excel cell + */ + public final DateFormat getDateFormat(int pos) + { + XFRecord xfr = (XFRecord) xfRecords.get(pos); + + if (xfr.isDate()) + { + return xfr.getDateFormat(); + } + + FormatRecord fr = (FormatRecord) + formats.get(new Integer(xfr.getFormatRecord())); + + if (fr == null) + { + return null; + } + + return fr.isDate() ? fr.getDateFormat() : null; + } + + /** + * Gets the NumberFormat used to format the cell. + * + * @param pos the xf format index + * @return the DateFormat object used to format the date in the original + * excel cell + */ + public final NumberFormat getNumberFormat(int pos) + { + XFRecord xfr = (XFRecord) xfRecords.get(pos); + + if (xfr.isNumber()) + { + return xfr.getNumberFormat(); + } + + FormatRecord fr = (FormatRecord) + formats.get(new Integer(xfr.getFormatRecord())); + + if (fr == null) + { + return null; + } + + return fr.isNumber() ? fr.getNumberFormat() : null; + } + + /** + * Gets the format record + * + * @param index the formatting record index to retrieve + * @return the format record at the specified index + */ + FormatRecord getFormatRecord(int index) + { + return (FormatRecord) + formats.get(new Integer(index)); + } + /** + * Writes out all the format records and the XF records + * + * @param outputFile the file to write to + * @exception IOException + */ + public void write(File outputFile) throws IOException + { + // Write out all the formats + Iterator i = formatsList.iterator(); + FormatRecord fr = null; + while (i.hasNext()) + { + fr = (FormatRecord) i.next(); + outputFile.write(fr); + } + + // Write out the styles + i = xfRecords.iterator(); + XFRecord xfr = null; + while (i.hasNext()) + { + xfr = (XFRecord) i.next(); + outputFile.write(xfr); + } + + // Write out the style records + BuiltInStyle style = new BuiltInStyle(0x10, 3); + outputFile.write(style); + + style = new BuiltInStyle(0x11, 6); + outputFile.write(style); + + style = new BuiltInStyle(0x12, 4); + outputFile.write(style); + + style = new BuiltInStyle(0x13, 7); + outputFile.write(style); + + style = new BuiltInStyle(0x0, 0); + outputFile.write(style); + + style = new BuiltInStyle(0x14, 5); + outputFile.write(style); + } + + /** + * Accessor for the fonts used by this workbook + * + * @return the fonts container + */ + protected final Fonts getFonts() + { + return fonts; + } + + /** + * Gets the XFRecord for the specified index. Used when copying individual + * cells + * + * @param index the XF record to retrieve + * @return the XF record at the specified index + */ + public final XFRecord getXFRecord(int index) + { + return (XFRecord) xfRecords.get(index); + } + + /** + * Gets the number of formatting records on the list. This is used by the + * writable subclass because there is an upper limit on the amount of + * format records that are allowed to be present in an excel sheet + * + * @return the number of format records present + */ + protected final int getNumberOfFormatRecords() + { + return formatsList.size(); + } + + /** + * Rationalizes all the fonts, removing duplicate entries + * + * @return the list of new font index number + */ + public IndexMapping rationalizeFonts() + { + return fonts.rationalize(); + } + + /** + * Rationalizes the cell formats. Duplicate + * formats are removed and the format indexed of the cells + * adjusted accordingly + * + * @param fontMapping the font mapping index numbers + * @param formatMapping the format mapping index numbers + * @return the list of new font index number + */ + public IndexMapping rationalize(IndexMapping fontMapping, + IndexMapping formatMapping) + { + // Update the index codes for the XF records using the format + // mapping and the font mapping + // at the same time + XFRecord xfr = null; + for (Iterator it = xfRecords.iterator(); it.hasNext();) + { + xfr = (XFRecord) it.next(); + + if (xfr.getFormatRecord() >= customFormatStartIndex) + { + xfr.setFormatIndex(formatMapping.getNewIndex(xfr.getFormatRecord())); + } + + xfr.setFontIndex(fontMapping.getNewIndex(xfr.getFontIndex())); + } + + ArrayList newrecords = new ArrayList(minXFRecords); + IndexMapping mapping = new IndexMapping(xfRecords.size()); + int numremoved = 0; + + int numXFRecords = Math.min(minXFRecords, xfRecords.size()); + // Copy across the fundamental styles + for (int i = 0; i < numXFRecords; i++) + { + newrecords.add(xfRecords.get(i)); + mapping.setMapping(i, i); + } + + if (numXFRecords < minXFRecords) + { + logger.warn("There are less than the expected minimum number of " + + "XF records"); + return mapping; + } + + // Iterate through the old list + for (int i = minXFRecords; i < xfRecords.size(); i++) + { + XFRecord xf = (XFRecord) xfRecords.get(i); + + // Compare against formats already on the list + boolean duplicate = false; + for (Iterator it = newrecords.iterator(); + it.hasNext() && !duplicate;) + { + XFRecord xf2 = (XFRecord) it.next(); + if (xf2.equals(xf)) + { + duplicate = true; + mapping.setMapping(i, mapping.getNewIndex(xf2.getXFIndex())); + numremoved++; + } + } + + // If this format is not a duplicate then add it to the new list + if (!duplicate) + { + newrecords.add(xf); + mapping.setMapping(i, i - numremoved); + } + } + + // It is sufficient to merely change the xf index field on all XFRecords + // In this case, CellValues which refer to defunct format records + // will nevertheless be written out with the correct index number + for (Iterator i = xfRecords.iterator(); i.hasNext();) + { + XFRecord xf = (XFRecord) i.next(); + xf.rationalize(mapping); + } + + // Set the new list + xfRecords = newrecords; + + return mapping; + } + + /** + * Rationalizes the display formats. Duplicate + * formats are removed and the format indices of the cells + * adjusted accordingly. It is invoked immediately prior to writing + * writing out the sheet + * @return the index mapping between the old display formats and the + * rationalized ones + */ + public IndexMapping rationalizeDisplayFormats() + { + ArrayList newformats = new ArrayList(); + int numremoved = 0; + IndexMapping mapping = new IndexMapping(nextCustomIndexNumber); + + // Iterate through the old list + Iterator i = formatsList.iterator(); + DisplayFormat df = null; + DisplayFormat df2 = null; + boolean duplicate = false; + while (i.hasNext()) + { + df = (DisplayFormat) i.next(); + + Assert.verify(!df.isBuiltIn()); + + // Compare against formats already on the list + Iterator i2 = newformats.iterator(); + duplicate = false; + while (i2.hasNext() && !duplicate) + { + df2 = (DisplayFormat) i2.next(); + if (df2.equals(df)) + { + duplicate = true; + mapping.setMapping(df.getFormatIndex(), + mapping.getNewIndex(df2.getFormatIndex())); + numremoved++; + } + } + + // If this format is not a duplicate then add it to the new list + if (!duplicate) + { + newformats.add(df); + int indexnum = df.getFormatIndex() - numremoved; + if (indexnum > maxFormatRecordsIndex) + { + logger.warn("Too many number formats - using default format."); + indexnum = 0; // the default number format index + } + mapping.setMapping(df.getFormatIndex(), + df.getFormatIndex() - numremoved); + } + } + + // Set the new list + formatsList = newformats; + + // Update the index codes for the remaining formats + i = formatsList.iterator(); + + while (i.hasNext()) + { + df = (DisplayFormat) i.next(); + df.initialize(mapping.getNewIndex(df.getFormatIndex())); + } + + return mapping; + } + + /** + * Accessor for the colour palette + * + * @return the palette + */ + public PaletteRecord getPalette() + { + return palette; + } + + /** + * Called from the WorkbookParser to set the colour palette + * + * @param pr the palette + */ + public void setPalette(PaletteRecord pr) + { + palette = pr; + } + + /** + * Sets the RGB value for the specified colour for this workbook + * + * @param c the colour whose RGB value is to be overwritten + * @param r the red portion to set (0-255) + * @param g the green portion to set (0-255) + * @param b the blue portion to set (0-255) + */ + public void setColourRGB(Colour c, int r, int g, int b) + { + if (palette == null) + { + palette = new PaletteRecord(); + } + palette.setColourRGB(c, r, g, b); + } + + /** + * Accessor for the RGB value for the specified colour + * + * @return the RGB for the specified colour + */ + public RGB getColourRGB(Colour c) + { + if (palette == null) + { + return c.getDefaultRGB(); + } + + return palette.getColourRGB(c); + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/FormulaData.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/FormulaData.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/FormulaData.java 17 Aug 2012 14:51:14 -0000 1.1 @@ -0,0 +1,40 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +import jxl.Cell; +import jxl.biff.formula.FormulaException; + +/** + * Interface which is used for copying formulas from a read only + * to a writable spreadsheet + */ +public interface FormulaData extends Cell +{ + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array EXCLUDING the standard cell information + * (row, column, xfindex) + * + * @return the raw record data + * @exception FormulaException + */ + public byte[] getFormulaData() throws FormulaException; +} Index: 3rdParty_sources/jexcelapi/jxl/biff/HeaderFooter.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/HeaderFooter.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/HeaderFooter.java 17 Aug 2012 14:51:14 -0000 1.1 @@ -0,0 +1,654 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan, Eric Jung + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import common.Logger; + +/** + * Class which represents an Excel header or footer. Information for this + * class came from Microsoft Knowledge Base Article 142136 + * (previously Q142136). + * + * This class encapsulates three internal structures representing the header + * or footer contents which appear on the left, right or central part of the + * page + */ +public abstract class HeaderFooter +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(HeaderFooter.class); + + // Codes to format text + + /** + * Turns bold printing on or off + */ + private static final String BOLD_TOGGLE = "&B"; + + /** + * Turns underline printing on or off + */ + private static final String UNDERLINE_TOGGLE = "&U"; + + /** + * Turns italic printing on or off + */ + private static final String ITALICS_TOGGLE = "&I"; + + /** + * Turns strikethrough printing on or off + */ + private static final String STRIKETHROUGH_TOGGLE = "&S"; + + /** + * Turns double-underline printing on or off + */ + private static final String DOUBLE_UNDERLINE_TOGGLE = "&E"; + + /** + * Turns superscript printing on or off + */ + private static final String SUPERSCRIPT_TOGGLE = "&X"; + + /** + * Turns subscript printing on or off + */ + private static final String SUBSCRIPT_TOGGLE = "&Y"; + + /** + * Turns outline printing on or off (Macintosh only) + */ + private static final String OUTLINE_TOGGLE = "&O"; + + /** + * Turns shadow printing on or off (Macintosh only) + */ + private static final String SHADOW_TOGGLE = "&H"; + + /** + * Left-aligns the characters that follow + */ + private static final String LEFT_ALIGN = "&L"; + + /** + * Centres the characters that follow + */ + private static final String CENTRE = "&C"; + + /** + * Right-aligns the characters that follow + */ + private static final String RIGHT_ALIGN = "&R"; + + // Codes to insert specific data + + /** + * Prints the page number + */ + private static final String PAGENUM = "&P"; + + /** + * Prints the total number of pages in the document + */ + private static final String TOTAL_PAGENUM = "&N"; + + /** + * Prints the current date + */ + private static final String DATE = "&D"; + + /** + * Prints the current time + */ + private static final String TIME = "&T"; + + /** + * Prints the name of the workbook + */ + private static final String WORKBOOK_NAME = "&F"; + + /** + * Prints the name of the worksheet + */ + private static final String WORKSHEET_NAME = "&A"; + + /** + * The contents - a simple wrapper around a string buffer + */ + protected static class Contents + { + /** + * The buffer containing the header/footer string + */ + private StringBuffer contents; + + /** + * The constructor + */ + protected Contents() + { + contents = new StringBuffer(); + } + + /** + * Constructor used when reading worksheets. The string contains all + * the formatting (but not alignment characters + * + * @param s the format string + */ + protected Contents(String s) + { + contents = new StringBuffer(s); + } + + /** + * Copy constructor + * + * @param copy the contents to copy + */ + protected Contents(Contents copy) + { + contents = new StringBuffer(copy.getContents()); + } + + /** + * Retrieves a Stringified + * version of this object + * + * @return the header string + */ + protected String getContents() + { + return contents != null ? contents.toString() : ""; + } + + /** + * Internal method which appends the text to the string buffer + * + * @param txt + */ + private void appendInternal(String txt) + { + if (contents == null) + { + contents = new StringBuffer(); + } + + contents.append(txt); + } + + /** + * Internal method which appends the text to the string buffer + * + * @param ch + */ + private void appendInternal(char ch) + { + if (contents == null) + { + contents = new StringBuffer(); + } + + contents.append(ch); + } + + /** + * Appends the text to the string buffer + * + * @param txt + */ + protected void append(String txt) + { + appendInternal(txt); + } + + /** + * Turns bold printing on or off. Bold printing + * is initially off. Text subsequently appended to + * this object will be bolded until this method is + * called again. + */ + protected void toggleBold() + { + appendInternal(BOLD_TOGGLE); + } + + /** + * Turns underline printing on or off. Underline printing + * is initially off. Text subsequently appended to + * this object will be underlined until this method is + * called again. + */ + protected void toggleUnderline() + { + appendInternal(UNDERLINE_TOGGLE); + } + + /** + * Turns italics printing on or off. Italics printing + * is initially off. Text subsequently appended to + * this object will be italicized until this method is + * called again. + */ + protected void toggleItalics() + { + appendInternal(ITALICS_TOGGLE); + } + + /** + * Turns strikethrough printing on or off. Strikethrough printing + * is initially off. Text subsequently appended to + * this object will be striked out until this method is + * called again. + */ + protected void toggleStrikethrough() + { + appendInternal(STRIKETHROUGH_TOGGLE); + } + + /** + * Turns double-underline printing on or off. Double-underline printing + * is initially off. Text subsequently appended to + * this object will be double-underlined until this method is + * called again. + */ + protected void toggleDoubleUnderline() + { + appendInternal(DOUBLE_UNDERLINE_TOGGLE); + } + + /** + * Turns superscript printing on or off. Superscript printing + * is initially off. Text subsequently appended to + * this object will be superscripted until this method is + * called again. + */ + protected void toggleSuperScript() + { + appendInternal(SUPERSCRIPT_TOGGLE); + } + + /** + * Turns subscript printing on or off. Subscript printing + * is initially off. Text subsequently appended to + * this object will be subscripted until this method is + * called again. + */ + protected void toggleSubScript() + { + appendInternal(SUBSCRIPT_TOGGLE); + } + + /** + * Turns outline printing on or off (Macintosh only). + * Outline printing is initially off. Text subsequently appended + * to this object will be outlined until this method is + * called again. + */ + protected void toggleOutline() + { + appendInternal(OUTLINE_TOGGLE); + } + + /** + * Turns shadow printing on or off (Macintosh only). + * Shadow printing is initially off. Text subsequently appended + * to this object will be shadowed until this method is + * called again. + */ + protected void toggleShadow() + { + appendInternal(SHADOW_TOGGLE); + } + + /** + * Sets the font of text subsequently appended to this + * object.. Previously appended text is not affected. + *

+ * Note: no checking is performed to + * determine if fontName is a valid font. + * + * @param fontName name of the font to use + */ + protected void setFontName(String fontName) + { + // Font name must be in quotations + appendInternal("&\""); + appendInternal(fontName); + appendInternal('\"'); + } + + /** + * Sets the font size of text subsequently appended to this + * object. Previously appended text is not affected. + *

+ * Valid point sizes are between 1 and 99 (inclusive). If + * size is outside this range, this method returns false + * and does not change font size. If size is within this + * range, the font size is changed and true is returned. + * + * @param size The size in points. Valid point sizes are + * between 1 and 99 (inclusive). + * @return true if the font size was changed, false if font + * size was not changed because 1 > size > 99. + */ + protected boolean setFontSize(int size) + { + if (size < 1 || size > 99) + { + return false; + } + + // A two digit number should be used -- even if the + // leading number is just a zero. + String fontSize; + if (size < 10) + { + // single-digit -- make two digit + fontSize = "0" + size; + } + else + { + fontSize = Integer.toString(size); + } + + appendInternal('&'); + appendInternal(fontSize); + return true; + } + + /** + * Appends the page number + */ + protected void appendPageNumber() + { + appendInternal(PAGENUM); + } + + /** + * Appends the total number of pages + */ + protected void appendTotalPages() + { + appendInternal(TOTAL_PAGENUM); + } + + /** + * Appends the current date + */ + protected void appendDate() + { + appendInternal(DATE); + } + + /** + * Appends the current time + */ + protected void appendTime() + { + appendInternal(TIME); + } + + /** + * Appends the workbook name + */ + protected void appendWorkbookName() + { + appendInternal(WORKBOOK_NAME); + } + + /** + * Appends the worksheet name + */ + protected void appendWorkSheetName() + { + appendInternal(WORKSHEET_NAME); + } + + /** + * Clears the contents of this portion + */ + protected void clear() + { + contents = null; + } + + /** + * Queries if the contents are empty + * + * @return TRUE if the contents are empty, FALSE otherwise + */ + protected boolean empty() + { + if (contents == null || contents.length() == 0) + { + return true; + } + else + { + return false; + } + } + } + + /** + * The left aligned header/footer contents + */ + private Contents left; + + /** + * The right aligned header/footer contents + */ + private Contents right; + + /** + * The centrally aligned header/footer contents + */ + private Contents centre; + + /** + * Default constructor. + */ + protected HeaderFooter() + { + left = createContents(); + right = createContents(); + centre = createContents(); + } + + /** + * Copy constructor + * + * @param c the item to copy + */ + protected HeaderFooter(HeaderFooter hf) + { + left = createContents(hf.left); + right = createContents(hf.right); + centre = createContents(hf.centre); + } + + /** + * Constructor used when reading workbooks to separate the left, right + * a central part of the strings into their constituent parts + */ + protected HeaderFooter(String s) + { + if (s == null || s.length() == 0) + { + left = createContents(); + right = createContents(); + centre = createContents(); + return; + } + + int pos = 0; + int leftPos = s.indexOf(LEFT_ALIGN); + int rightPos = s.indexOf(RIGHT_ALIGN); + int centrePos = s.indexOf(CENTRE); + + // Do the left position string + if (pos == leftPos) + { + if (centrePos != -1) + { + left = createContents(s.substring(pos + 2, centrePos)); + pos = centrePos; + } + else if (rightPos != -1 ) + { + left = createContents(s.substring(pos + 2, rightPos)); + pos = rightPos; + } + else + { + left = createContents(s.substring(pos + 2)); + pos = s.length(); + } + } + + // Do the centrally positioned part of the string. This is the default + // if no alignment string is specified + if (pos == centrePos || + (leftPos == -1 && rightPos == -1 && centrePos == -1)) + { + if (rightPos != -1) + { + centre = createContents(s.substring(pos + 2, rightPos)); + pos = rightPos; + } + else + { + int cpos = (pos == centrePos) ? pos + 2 : pos; + centre = createContents(s.substring(cpos)); + pos = s.length(); + } + } + + // Do the right positioned part of the string + if (pos == rightPos) + { + right = createContents(s.substring(pos + 2)); + pos = s.length(); + } + + if (left == null) + { + left = createContents(); + } + + if (centre == null) + { + centre = createContents(); + } + + if (right == null) + { + right = createContents(); + } + } + + /** + * Retrieves a Stringified + * version of this object + * + * @return the header string + */ + public String toString() + { + StringBuffer hf = new StringBuffer(); + if (!left.empty()) + { + hf.append(LEFT_ALIGN); + hf.append(left.getContents()); + } + + if (!centre.empty()) + { + hf.append(CENTRE); + hf.append(centre.getContents()); + } + + if (!right.empty()) + { + hf.append(RIGHT_ALIGN); + hf.append(right.getContents()); + } + + return hf.toString(); + } + + /** + * Accessor for the contents which appear on the right hand side of the page + * + * @return the right aligned contents + */ + protected Contents getRightText() + { + return right; + } + + /** + * Accessor for the contents which in the centre of the page + * + * @return the centrally aligned contents + */ + protected Contents getCentreText() + { + return centre; + } + + /** + * Accessor for the contents which appear on the left hand side of the page + * + * @return the left aligned contents + */ + protected Contents getLeftText() + { + return left; + } + + /** + * Clears the contents of the header/footer + */ + protected void clear() + { + left.clear(); + right.clear(); + centre.clear(); + } + + /** + * Creates internal class of the appropriate type + */ + protected abstract Contents createContents(); + + /** + * Creates internal class of the appropriate type + */ + protected abstract Contents createContents(String s); + + /** + * Creates internal class of the appropriate type + */ + protected abstract Contents createContents(Contents c); +} Index: 3rdParty_sources/jexcelapi/jxl/biff/IndexMapping.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/IndexMapping.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/IndexMapping.java 17 Aug 2012 14:51:13 -0000 1.1 @@ -0,0 +1,70 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +import common.Logger; + +/** + * This class is a wrapper for a list of mappings between indices. + * It is used when removing duplicate records and specifies the new + * index for cells which have the duplicate format + */ +public final class IndexMapping +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(IndexMapping.class); + + /** + * The array of new indexes for an old one + */ + private int[] newIndices; + + /** + * Constructor + * + * @param size the number of index numbers to be mapped + */ + public IndexMapping(int size) + { + newIndices = new int[size]; + } + + /** + * Sets a mapping + * @param oldIndex the old index + * @param newIndex the new index + */ + public void setMapping(int oldIndex, int newIndex) + { + newIndices[oldIndex] = newIndex; + } + + /** + * Gets the new cell format index + * @param oldIndex the existing index number + * @return the new index number + */ + public int getNewIndex(int oldIndex) + { + return newIndices[oldIndex]; + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/IntegerHelper.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/IntegerHelper.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/IntegerHelper.java 17 Aug 2012 14:51:14 -0000 1.1 @@ -0,0 +1,149 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +/** + * Converts excel byte representations into integers + */ +public final class IntegerHelper +{ + /** + * Private constructor disables the instantiation of this object + */ + private IntegerHelper() + { + } + + /** + * Gets an int from two bytes + * + * @param b2 the second byte + * @param b1 the first byte + * @return The integer value + */ + public static int getInt(byte b1, byte b2) + { + int i1 = b1 & 0xff; + int i2 = b2 & 0xff; + int val = i2 << 8 | i1; + return val; + } + + /** + * Gets an short from two bytes + * + * @param b2 the second byte + * @param b1 the first byte + * @return The short value + */ + public static short getShort(byte b1, byte b2) + { + short i1 = (short) (b1 & 0xff); + short i2 = (short) (b2 & 0xff); + short val = (short) (i2 << 8 | i1); + return val; + } + + + /** + * Gets an int from four bytes, doing all the necessary swapping + * + * @param b1 a byte + * @param b2 a byte + * @param b3 a byte + * @param b4 a byte + * @return the integer value represented by the four bytes + */ + public static int getInt(byte b1, byte b2, byte b3, byte b4) + { + int i1 = getInt(b1, b2); + int i2 = getInt(b3, b4); + + int val = i2 << 16 | i1; + return val; + } + + /** + * Gets a two byte array from an integer + * + * @param i the integer + * @return the two bytes + */ + public static byte[] getTwoBytes(int i) + { + byte[] bytes = new byte[2]; + + bytes[0] = (byte) (i & 0xff); + bytes[1] = (byte) ((i & 0xff00) >> 8); + + return bytes; + } + + /** + * Gets a four byte array from an integer + * + * @param i the integer + * @return a four byte array + */ + public static byte[] getFourBytes(int i) + { + byte[] bytes = new byte[4]; + + int i1 = i & 0xffff; + int i2 = (i & 0xffff0000) >> 16; + + getTwoBytes(i1, bytes, 0); + getTwoBytes(i2, bytes, 2); + + return bytes; + } + + + /** + * Converts an integer into two bytes, and places it in the array at the + * specified position + * + * @param target the array to place the byte data into + * @param pos the position at which to place the data + * @param i the integer value to convert + */ + public static void getTwoBytes(int i, byte[] target, int pos) + { + target[pos] = (byte) (i & 0xff); + target[pos + 1] = (byte) ((i & 0xff00) >> 8); + } + + /** + * Converts an integer into four bytes, and places it in the array at the + * specified position + * + * @param target the array which is to contain the converted data + * @param pos the position in the array in which to place the data + * @param i the integer to convert + */ + public static void getFourBytes(int i, byte[] target, int pos) + { + byte[] bytes = getFourBytes(i); + target[pos] = bytes[0]; + target[pos + 1] = bytes[1]; + target[pos + 2] = bytes[2]; + target[pos + 3] = bytes[3]; + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/NumFormatRecordsException.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/NumFormatRecordsException.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/NumFormatRecordsException.java 17 Aug 2012 14:51:14 -0000 1.1 @@ -0,0 +1,36 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +/** + * Excel places a constraint on the number of format records that + * are allowed. This exception is thrown when that number is exceeded + * This is a static exception and should be handled internally + */ +public class NumFormatRecordsException extends Exception +{ + /** + * Constructor + */ + public NumFormatRecordsException() + { + super("Internal error: max number of FORMAT records exceeded"); + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/PaletteRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/PaletteRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/PaletteRecord.java 17 Aug 2012 14:51:13 -0000 1.1 @@ -0,0 +1,228 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +import jxl.format.Colour; +import jxl.format.RGB; +import jxl.read.biff.Record; + +/** + * A record representing the RGB colour palette + */ +public class PaletteRecord extends WritableRecordData +{ + /** + * The list of bespoke rgb colours used by this sheet + */ + private RGB[] rgbColours = new RGB[numColours]; + + /** + * A dirty flag indicating that this palette has been tampered with + * in some way + */ + private boolean dirty; + + /** + * Flag indicating that the palette was read in + */ + private boolean read; + + /** + * Initialized flag + */ + private boolean initialized; + + /** + * The number of colours in the palette + */ + private static final int numColours = 56; + + /** + * Constructor + * + * @param t the raw bytes + */ + public PaletteRecord(Record t) + { + super(t); + + initialized = false; + dirty = false; + read = true; + } + + /** + * Default constructor - used when there is no palette specified + */ + public PaletteRecord() + { + super(Type.PALETTE); + + initialized = true; + dirty = false; + read = false; + + // Initialize the array with all the default colours + Colour[] colours = Colour.getAllColours(); + + for (int i = 0; i < colours.length; i++) + { + Colour c = colours[i]; + setColourRGB(c, + c.getDefaultRGB().getRed(), + c.getDefaultRGB().getGreen(), + c.getDefaultRGB().getBlue()); + } + } + + /** + * Accessor for the binary data - used when copying + * + * @return the binary data + */ + public byte[] getData() + { + // Palette was read in, but has not been changed + if (read && !dirty) + { + return getRecord().getData(); + } + + byte[] data = new byte[numColours * 4 + 2]; + int pos = 0; + + // Set the number of records + IntegerHelper.getTwoBytes(numColours, data, pos); + + // Set the rgb content + for (int i = 0; i < numColours; i++) + { + pos = i * 4 + 2; + data[pos] = (byte) rgbColours[i].getRed(); + data[pos + 1] = (byte) rgbColours[i].getGreen(); + data[pos + 2] = (byte) rgbColours[i].getBlue(); + } + + return data; + } + + /** + * Initialize the record data + */ + private void initialize() + { + byte[] data = getRecord().getData(); + + int numrecords = IntegerHelper.getInt(data[0], data[1]); + + for (int i = 0; i < numrecords; i++) + { + int pos = i * 4 + 2; + int red = IntegerHelper.getInt(data[pos], (byte) 0); + int green = IntegerHelper.getInt(data[pos + 1], (byte) 0); + int blue = IntegerHelper.getInt(data[pos + 2], (byte) 0); + rgbColours[i] = new RGB(red, green, blue); + } + + initialized = true; + } + + /** + * Accessor for the dirty flag, which indicates if this palette has been + * modified + * + * @return TRUE if the palette has been modified, FALSE if it is the default + */ + public boolean isDirty() + { + return dirty; + } + + /** + * Sets the RGB value for the specified colour for this workbook + * + * @param c the colour whose RGB value is to be overwritten + * @param r the red portion to set (0-255) + * @param g the green portion to set (0-255) + * @param b the blue portion to set (0-255) + */ + public void setColourRGB(Colour c, int r, int g, int b) + { + // Only colours on the standard palette with values 8-64 are acceptable + int pos = c.getValue() - 8; + if (pos < 0 || pos >= numColours) + { + return; + } + + if (!initialized) + { + initialize(); + } + + // Force the colours into the range 0-255 + r = setValueRange(r, 0, 0xff); + g = setValueRange(g, 0, 0xff); + b = setValueRange(b, 0, 0xff); + + rgbColours[pos] = new RGB(r, g, b); + + // Indicate that the palette has been modified + dirty = true; + } + + /** + * Gets the colour RGB from the palette + * + * @param c the colour + * @return an RGB structure + */ + public RGB getColourRGB(Colour c) + { + // Only colours on the standard palette with values 8-64 are acceptable + int pos = c.getValue() - 8; + if (pos < 0 || pos >= numColours) + { + return c.getDefaultRGB(); + } + + if (!initialized) + { + initialize(); + } + + return rgbColours[pos]; + } + + /** + * Forces the value passed in to be between the range passed in + * + * @param val the value to constrain + * @param min the minimum acceptable value + * @param max the maximum acceptable value + * @return the constrained value + */ + private int setValueRange(int val, int min, int max) + { + val = Math.max(val, min); + val = Math.min(val, max); + return val; + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/RangeImpl.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/RangeImpl.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/RangeImpl.java 17 Aug 2012 14:51:14 -0000 1.1 @@ -0,0 +1,169 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +import common.Logger; + +import jxl.Cell; +import jxl.Range; +import jxl.Sheet; + +/** + * Implementation class for the Range interface. This merely + * holds the raw range information, and when the time comes, it + * interrogates the workbook for the object. + * This does not keep handles to the objects for performance reasons, + * as this could impact garbage collection on larger spreadsheets + */ +public class RangeImpl implements Range +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(RangeImpl.class); + + /** + * A handle to the workbook + */ + private WorkbookMethods workbook; + + /** + * The sheet index containing the column at the top left + */ + private int sheet1; + + /** + * The column number of the cell at the top left of the range + */ + private int column1; + + /** + * The row number of the cell at the top left of the range + */ + private int row1; + + /** + * The sheet index of the cell at the bottom right + */ + private int sheet2; + + /** + * The column index of the cell at the bottom right + */ + private int column2; + + /** + * The row index of the cell at the bottom right + */ + private int row2; + + /** + * Constructor + * @param w the workbook + * @param es the external sheet + * @param s1 the sheet of the top left cell of the range + * @param c1 the column number of the top left cell of the range + * @param r1 the row number of the top left cell of the range + * @param s2 the sheet of the bottom right cell + * @param c2 the column number of the bottom right cell of the range + * @param r2 the row number of the bottomr right cell of the range + */ + public RangeImpl(WorkbookMethods w, + int s1, int c1, int r1, + int s2, int c2, int r2) + { + workbook = w; + sheet1 = s1; + sheet2 = s2; + row1 = r1; + row2 = r2; + column1 = c1; + column2 = c2; + } + + /** + * Gets the cell at the top left of this range + * + * @return the cell at the top left + */ + public Cell getTopLeft() + { + Sheet s = workbook.getReadSheet(sheet1); + + if (column1 < s.getColumns() && + row1 < s.getRows()) + { + return s.getCell(column1, row1); + } + else + { + return new EmptyCell(column1, row1); + } + } + + /** + * Gets the cell at the bottom right of this range + * + * @return the cell at the bottom right + */ + public Cell getBottomRight() + { + Sheet s = workbook.getReadSheet(sheet2); + + if (column2 < s.getColumns() && + row2 < s.getRows()) + { + return s.getCell(column2, row2); + } + else + { + return new EmptyCell(column2, row2); + } + } + + /** + * Gets the index of the first sheet in the range + * + * @return the index of the first sheet in the range + */ + public int getFirstSheetIndex() + { + return sheet1; + } + + /** + * Gets the index of the last sheet in the range + * + * @return the index of the last sheet in the range + */ + public int getLastSheetIndex() + { + return sheet2; + } +} + + + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/biff/RecordData.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/RecordData.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/RecordData.java 17 Aug 2012 14:51:14 -0000 1.1 @@ -0,0 +1,88 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +import jxl.read.biff.Record; + +/** + * The record data within a record + */ +public abstract class RecordData +{ + /** + * The raw data + */ + private Record record; + + /** + * The Biff code for this record. This is set up when the record is + * used for writing + */ + private int code; + + /** + * Constructs this object from the raw data + * + * @param r the raw data + */ + protected RecordData(Record r) + { + record = r; + code = r.getCode(); + } + + /** + * Constructor used by the writable records + * + * @param t the type + */ + protected RecordData(Type t) + { + code = t.value; + } + + /** + * Returns the raw data to its subclasses + * + * @return the raw data + */ + protected Record getRecord() + { + return record; + } + + /** + * Accessor for the code + * + * @return the code + */ + protected final int getCode() + { + return code; + } +} + + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/biff/SheetRangeImpl.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/SheetRangeImpl.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/SheetRangeImpl.java 17 Aug 2012 14:51:13 -0000 1.1 @@ -0,0 +1,329 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +import jxl.Cell; +import jxl.Range; +import jxl.Sheet; + +/** + * Implementation class for the Range interface. This merely + * holds the raw range information. This implementation is used + * for ranges which are present on the current working sheet, so the + * getSheetIndex merely returns -1 + */ +public class SheetRangeImpl implements Range +{ + /** + * A handle to the sheet containing this range + */ + private Sheet sheet; + + /** + * The column number of the cell at the top left of the range + */ + private int column1; + + /** + * The row number of the cell at the top left of the range + */ + private int row1; + + /** + * The column index of the cell at the bottom right + */ + private int column2; + + /** + * The row index of the cell at the bottom right + */ + private int row2; + + /** + * Constructor + * @param s the sheet containing the range + * @param c1 the column number of the top left cell of the range + * @param r1 the row number of the top left cell of the range + * @param c2 the column number of the bottom right cell of the range + * @param r2 the row number of the bottomr right cell of the range + */ + public SheetRangeImpl(Sheet s, int c1, int r1, + int c2, int r2) + { + sheet = s; + row1 = r1; + row2 = r2; + column1 = c1; + column2 = c2; + } + + /** + * A copy constructor used for copying ranges between sheets + * + * @param c the range to copy from + * @param s the writable sheet + */ + public SheetRangeImpl(SheetRangeImpl c, Sheet s) + { + sheet = s; + row1 = c.row1; + row2 = c.row2; + column1 = c.column1; + column2 = c.column2; + } + + /** + * Gets the cell at the top left of this range + * + * @return the cell at the top left + */ + public Cell getTopLeft() + { + // If the print area exceeds the bounds of the sheet, then handle + // it here. The sheet implementation will give a NPE + if (column1 >= sheet.getColumns() || + row1 >= sheet.getRows()) + { + return new EmptyCell(column1,row1); + } + + return sheet.getCell(column1, row1); + } + + /** + * Gets the cell at the bottom right of this range + * + * @return the cell at the bottom right + */ + public Cell getBottomRight() + { + // If the print area exceeds the bounds of the sheet, then handle + // it here. The sheet implementation will give a NPE + if (column2 >= sheet.getColumns() || + row2 >= sheet.getRows()) + { + return new EmptyCell(column2,row2); + } + + return sheet.getCell(column2, row2); + } + + /** + * Not supported. Returns -1, indicating that it refers to the current + * sheet + * + * @return -1 + */ + public int getFirstSheetIndex() + { + return -1; + } + + /** + * Not supported. Returns -1, indicating that it refers to the current + * sheet + * + * @return -1 + */ + public int getLastSheetIndex() + { + return -1; + } + + /** + * Sees whether there are any intersections between this range and the + * range passed in. This method is used internally by the WritableSheet to + * verify the integrity of merged cells, hyperlinks etc. Ranges are + * only ever compared for the same sheet + * + * @param range the range to compare against + * @return TRUE if the ranges intersect, FALSE otherwise + */ + public boolean intersects(SheetRangeImpl range) + { + if (range == this) + { + return true; + } + + if (row2 < range.row1 || + row1 > range.row2 || + column2 < range.column1 || + column1 > range.column2) + { + return false; + } + + return true; + } + + /** + * To string method - primarily used during debugging + * + * @return the string version of this object + */ + public String toString() + { + StringBuffer sb = new StringBuffer(); + CellReferenceHelper.getCellReference(column1, row1, sb); + sb.append('-'); + CellReferenceHelper.getCellReference(column2, row2, sb); + return sb.toString(); + } + + /** + * A row has been inserted, so adjust the range objects accordingly + * + * @param r the row which has been inserted + */ + public void insertRow(int r) + { + if (r > row2) + { + return; + } + + if (r <= row1) + { + row1++; + } + + if (r <= row2) + { + row2++; + } + } + + /** + * A column has been inserted, so adjust the range objects accordingly + * + * @param c the column which has been inserted + */ + public void insertColumn(int c) + { + if (c > column2) + { + return; + } + + if (c <= column1) + { + column1++; + } + + if (c <= column2) + { + column2++; + } + } + + /** + * A row has been removed, so adjust the range objects accordingly + * + * @param r the row which has been inserted + */ + public void removeRow(int r) + { + if (r > row2) + { + return; + } + + if (r < row1) + { + row1--; + } + + if (r < row2) + { + row2--; + } + } + + /** + * A column has been removed, so adjust the range objects accordingly + * + * @param c the column which has been removed + */ + public void removeColumn(int c) + { + if (c > column2) + { + return; + } + + if (c < column1) + { + column1--; + } + + if (c < column2) + { + column2--; + } + } + + /** + * Standard hash code method + * + * @return the hash code + */ + public int hashCode() + { + return 0xffff ^ row1 ^ row2 ^ column1 ^ column2; + } + + /** + * Standard equals method + * + * @param o the object to compare + * @return TRUE if the two objects are the same, FALSE otherwise + */ + public boolean equals(Object o) + { + if (o == this) + { + return true; + } + + if (!(o instanceof SheetRangeImpl)) + { + return false; + } + + SheetRangeImpl compare = (SheetRangeImpl) o; + + return (column1 == compare.column1 && + column2 == compare.column2 && + row1 == compare.row1 && + row2 == compare.row2); + } + +} + + + + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/biff/StringHelper.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/StringHelper.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/StringHelper.java 17 Aug 2012 14:51:14 -0000 1.1 @@ -0,0 +1,227 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +import java.io.UnsupportedEncodingException; + +import common.Logger; + +import jxl.WorkbookSettings; + +/** + * Helper function to convert Java string objects to and from the byte + * representations + */ +public final class StringHelper +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(StringHelper.class); + + // Due to a a Sun bug in some versions of JVM 1.4, the UnicodeLittle + // encoding doesn't always work. Making this a public static field + // enables client code access to this (but in an undocumented and + // unsupported fashion). Suggested alternative values for this + // are "UTF-16LE" or "UnicodeLittleUnmarked" + public static String UNICODE_ENCODING = "UnicodeLittle"; + + /** + * Private default constructor to prevent instantiation + */ + private StringHelper() + { + } + + /** + * Gets the bytes of the specified string. This will simply return the ASCII + * values of the characters in the string + * + * @param s the string to convert into bytes + * @return the ASCII values of the characters in the string + * @deprecated + */ + public static byte[] getBytes(String s) + { + return s.getBytes(); + } + + /** + * Gets the bytes of the specified string. This will simply return the ASCII + * values of the characters in the string + * + * @param s the string to convert into bytes + * @return the ASCII values of the characters in the string + */ + public static byte[] getBytes(String s, WorkbookSettings ws) + { + try + { + return s.getBytes(ws.getEncoding()); + } + catch (UnsupportedEncodingException e) + { + // fail silently + return null; + } + } + + /** + * Converts the string into a little-endian array of Unicode bytes + * + * @param s the string to convert + * @return the unicode values of the characters in the string + */ + public static byte[] getUnicodeBytes(String s) + { + try + { + byte[] b = s.getBytes(UNICODE_ENCODING); + + // Sometimes this method writes out the unicode + // identifier + if (b.length == (s.length() * 2 + 2)) + { + byte[] b2 = new byte[b.length - 2]; + System.arraycopy(b, 2, b2, 0, b2.length); + b = b2; + } + return b; + } + catch (UnsupportedEncodingException e) + { + // Fail silently + return null; + } + } + + + /** + * Gets the ASCII bytes from the specified string and places them in the + * array at the specified position + * + * @param pos the position at which to place the converted data + * @param s the string to convert + * @param d the byte array which will contain the converted string data + */ + public static void getBytes(String s, byte[] d, int pos) + { + byte[] b = getBytes(s); + System.arraycopy(b, 0, d, pos, b.length); + } + + /** + * Inserts the unicode byte representation of the specified string into the + * array passed in + * + * @param pos the position at which to insert the converted data + * @param s the string to convert + * @param d the byte array which will hold the string data + */ + public static void getUnicodeBytes(String s, byte[] d, int pos) + { + byte[] b = getUnicodeBytes(s); + System.arraycopy(b, 0, d, pos, b.length); + } + + /** + * Gets a string from the data array using the character encoding for + * this workbook + * + * @param pos The start position of the string + * @param length The number of characters in the string + * @param d The byte data + * @param ws the workbook settings + * @return the string built up from the raw bytes + */ + public static String getString(byte[] d, int length, int pos, + WorkbookSettings ws) + { + if( length == 0 ) + { + return ""; // Reduces number of new Strings + } + + try + { + return new String(d, pos, length, ws.getEncoding()); + // byte[] b = new byte[length]; + // System.arraycopy(d, pos, b, 0, length); + // return new String(b, ws.getEncoding()); + } + catch (UnsupportedEncodingException e) + { + logger.warn(e.toString()); + return ""; + } + } + + /** + * Gets a string from the data array + * + * @param pos The start position of the string + * @param length The number of characters in the string + * @param d The byte data + * @return the string built up from the unicode characters + */ + public static String getUnicodeString(byte[] d, int length, int pos) + { + try + { + byte[] b = new byte[length * 2]; + System.arraycopy(d, pos, b, 0, length * 2); + return new String(b, UNICODE_ENCODING); + } + catch (UnsupportedEncodingException e) + { + // Fail silently + return ""; + } + } + + /** + * Replaces all instances of search with replace in the input. + * Even though later versions of java can use string.replace() + * this is included Java 1.2 compatibility + * + * @param input the format string + * @param search the Excel character to be replaced + * @param replace the java equivalent + * @return the input string with the specified substring replaced + */ + public static final String replace(String input, + String search, + String replace) + { + String fmtstr = input; + int pos = fmtstr.indexOf(search); + while (pos != -1) + { + StringBuffer tmp = new StringBuffer(fmtstr.substring(0, pos)); + tmp.append(replace); + tmp.append(fmtstr.substring(pos + search.length())); + fmtstr = tmp.toString(); + pos = fmtstr.indexOf(search, pos+replace.length()); + } + return fmtstr; + } +} + + Index: 3rdParty_sources/jexcelapi/jxl/biff/Type.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/Type.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/Type.java 17 Aug 2012 14:51:13 -0000 1.1 @@ -0,0 +1,493 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +/** + * An enumeration class which contains the biff types + */ +public final class Type +{ + /** + * The biff value for this type + */ + public final int value; + /** + * An array of all types + */ + private static Type[] types = new Type[0]; + + /** + * Constructor + * Sets the biff value and adds this type to the array of all types + * + * @param v the biff code for the type + */ + private Type(int v) + { + value = v; + + // Add to the list of available types + Type[] newTypes = new Type[types.length + 1]; + System.arraycopy(types, 0, newTypes, 0, types.length); + newTypes[types.length] = this; + types = newTypes; + } + + private static class ArbitraryType {}; + private static ArbitraryType arbitrary = new ArbitraryType(); + + /** + * Constructor used for the creation of arbitrary types + */ + private Type(int v, ArbitraryType arb) + { + value = v; + } + + /** + * Standard hash code method + * @return the hash code + */ + public int hashCode() + { + return value; + } + + /** + * Standard equals method + * @param o the object to compare + * @return TRUE if the objects are equal, FALSE otherwise + */ + public boolean equals(Object o) + { + if (o == this) + { + return true; + } + + if (!(o instanceof Type)) + { + return false; + } + + Type t = (Type) o; + + return value == t.value; + } + + /** + * Gets the type object from its integer value + * @param v the internal code + * @return the type + */ + public static Type getType(int v) + { + for (int i = 0; i < types.length; i++) + { + if (types[i].value == v) + { + return types[i]; + } + } + + return UNKNOWN; + } + + /** + * Used to create an arbitrary record type. This method is only + * used during bespoke debugging process. The creation of an + * arbitrary type does not add it to the static list of known types + */ + public static Type createType(int v) + { + return new Type(v, arbitrary); + } + + /** + */ + public static final Type BOF = new Type(0x809); + /** + */ + public static final Type EOF = new Type(0x0a); + /** + */ + public static final Type BOUNDSHEET = new Type(0x85); + /** + */ + public static final Type SUPBOOK = new Type(0x1ae); + /** + */ + public static final Type EXTERNSHEET = new Type(0x17); + /** + */ + public static final Type DIMENSION = new Type(0x200); + /** + */ + public static final Type BLANK = new Type(0x201); + /** + */ + public static final Type MULBLANK = new Type(0xbe); + /** + */ + public static final Type ROW = new Type(0x208); + /** + */ + public static final Type NOTE = new Type(0x1c); + /** + */ + public static final Type TXO = new Type(0x1b6); + /** + */ + public static final Type RK = new Type(0x7e); + /** + */ + public static final Type RK2 = new Type(0x27e); + /** + */ + public static final Type MULRK = new Type(0xbd); + /** + */ + public static final Type INDEX = new Type(0x20b); + /** + */ + public static final Type DBCELL = new Type(0xd7); + /** + */ + public static final Type SST = new Type(0xfc); + /** + */ + public static final Type COLINFO = new Type(0x7d); + /** + */ + public static final Type EXTSST = new Type(0xff); + /** + */ + public static final Type CONTINUE = new Type(0x3c); + /** + */ + public static final Type LABEL = new Type(0x204); + /** + */ + public static final Type RSTRING = new Type(0xd6); + /** + */ + public static final Type LABELSST = new Type(0xfd); + /** + */ + public static final Type NUMBER = new Type(0x203); + /** + */ + public static final Type NAME = new Type(0x18); + /** + */ + public static final Type TABID = new Type(0x13d); + /** + */ + public static final Type ARRAY = new Type(0x221); + /** + */ + public static final Type STRING = new Type(0x207); + /** + */ + public static final Type FORMULA = new Type(0x406); + /** + */ + public static final Type FORMULA2 = new Type(0x6); + /** + */ + public static final Type SHAREDFORMULA = new Type(0x4bc); + /** + */ + public static final Type FORMAT = new Type(0x41e); + /** + */ + public static final Type XF = new Type(0xe0); + /** + */ + public static final Type BOOLERR = new Type(0x205); + /** + */ + public static final Type INTERFACEHDR = new Type(0xe1); + /** + */ + public static final Type SAVERECALC = new Type(0x5f); + /** + */ + public static final Type INTERFACEEND = new Type(0xe2); + /** + */ + public static final Type XCT = new Type(0x59); + /** + */ + public static final Type CRN = new Type(0x5a); + /** + */ + public static final Type DEFCOLWIDTH = new Type(0x55); + /** + */ + public static final Type DEFAULTROWHEIGHT = new Type(0x225); + /** + */ + public static final Type WRITEACCESS = new Type(0x5c); + /** + */ + public static final Type WSBOOL = new Type(0x81); + /** + */ + public static final Type CODEPAGE = new Type(0x42); + /** + */ + public static final Type DSF = new Type(0x161); + /** + */ + public static final Type FNGROUPCOUNT = new Type(0x9c); + /** + */ + public static final Type FILTERMODE = new Type(0x9b); + /** + */ + public static final Type AUTOFILTERINFO = new Type(0x9d); + /** + */ + public static final Type AUTOFILTER = new Type(0x9e); + /** + */ + public static final Type COUNTRY = new Type(0x8c); + /** + */ + public static final Type PROTECT = new Type(0x12); + /** + */ + public static final Type SCENPROTECT = new Type(0xdd); + /** + */ + public static final Type OBJPROTECT = new Type(0x63); + /** + */ + public static final Type PRINTHEADERS = new Type(0x2a); + /** + */ + public static final Type HEADER = new Type(0x14); + /** + */ + public static final Type FOOTER = new Type(0x15); + /** + */ + public static final Type HCENTER = new Type(0x83); + /** + */ + public static final Type VCENTER = new Type(0x84); + /** + */ + public static final Type FILEPASS = new Type(0x2f); + /** + */ + public static final Type SETUP = new Type(0xa1); + /** + */ + public static final Type PRINTGRIDLINES = new Type(0x2b); + /** + */ + public static final Type GRIDSET = new Type(0x82); + /** + */ + public static final Type GUTS = new Type(0x80); + /** + */ + public static final Type WINDOWPROTECT = new Type(0x19); + /** + */ + public static final Type PROT4REV = new Type(0x1af); + /** + */ + public static final Type PROT4REVPASS = new Type(0x1bc); + /** + */ + public static final Type PASSWORD = new Type(0x13); + /** + */ + public static final Type REFRESHALL = new Type(0x1b7); + /** + */ + public static final Type WINDOW1 = new Type(0x3d); + /** + */ + public static final Type WINDOW2 = new Type(0x23e); + /** + */ + public static final Type BACKUP = new Type(0x40); + /** + */ + public static final Type HIDEOBJ = new Type(0x8d); + /** + */ + public static final Type NINETEENFOUR = new Type(0x22); + /** + */ + public static final Type PRECISION = new Type(0xe); + /** + */ + public static final Type BOOKBOOL = new Type(0xda); + /** + */ + public static final Type FONT = new Type(0x31); + /** + */ + public static final Type MMS = new Type(0xc1); + /** + */ + public static final Type CALCMODE = new Type(0x0d); + /** + */ + public static final Type CALCCOUNT = new Type(0x0c); + /** + */ + public static final Type REFMODE = new Type(0x0f); + /** + */ + public static final Type TEMPLATE = new Type(0x60); + /** + */ + public static final Type OBJPROJ = new Type(0xd3); + /** + */ + public static final Type DELTA = new Type(0x10); + /** + */ + public static final Type MERGEDCELLS = new Type(0xe5); + /** + */ + public static final Type ITERATION = new Type(0x11); + /** + */ + public static final Type STYLE = new Type(0x293); + /** + */ + public static final Type USESELFS = new Type(0x160); + /** + */ + public static final Type VERTICALPAGEBREAKS = new Type(0x1a); + /** + */ + public static final Type HORIZONTALPAGEBREAKS = new Type(0x1b); + /** + */ + public static final Type SELECTION = new Type(0x1d); + /** + */ + public static final Type HLINK = new Type(0x1b8); + /** + */ + public static final Type OBJ = new Type(0x5d); + /** + */ + public static final Type MSODRAWING = new Type(0xec); + /** + */ + public static final Type MSODRAWINGGROUP = new Type(0xeb); + /** + */ + public static final Type LEFTMARGIN = new Type(0x26); + /** + */ + public static final Type RIGHTMARGIN = new Type(0x27); + /** + */ + public static final Type TOPMARGIN = new Type(0x28); + /** + */ + public static final Type BOTTOMMARGIN = new Type(0x29); + /** + */ + public static final Type EXTERNNAME = new Type(0x23); + /** + */ + public static final Type PALETTE = new Type(0x92); + /** + */ + public static final Type PLS = new Type(0x4d); + /** + */ + public static final Type SCL = new Type(0xa0); + /** + */ + public static final Type PANE = new Type(0x41); + /** + */ + public static final Type WEIRD1 = new Type(0xef); + /** + */ + public static final Type SORT = new Type(0x90); + /** + */ + public static final Type CONDFMT = new Type(0x1b0); + /** + */ + public static final Type CF = new Type(0x1b1); + /** + */ + public static final Type DV = new Type(0x1be); + /** + */ + public static final Type DVAL = new Type(0x1b2); + /** + */ + public static final Type BUTTONPROPERTYSET = new Type(0x1ba); + + // Chart types + /** + */ + public static final Type FONTX = new Type(0x1026); + /** + */ + public static final Type IFMT = new Type(0x104e); + /** + */ + public static final Type FBI = new Type(0x1060); + /** + */ + public static final Type ALRUNS = new Type(0x1050); + /** + */ + public static final Type SERIES = new Type(0x1003); + /** + */ + public static final Type SERIESLIST = new Type(0x1016); + /** + */ + public static final Type SBASEREF = new Type(0x1048); + /** + */ + public static final Type UNKNOWN = new Type(0xffff); + + // Unknown types + public static final Type U1C0 = new Type(0x1c0); + public static final Type U1C1 = new Type(0x1c1); + +} + + + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/biff/WorkbookMethods.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/WorkbookMethods.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/WorkbookMethods.java 17 Aug 2012 14:51:14 -0000 1.1 @@ -0,0 +1,53 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +import jxl.Sheet; +/** + * An interface containing some common workbook methods. This so that + * objects which are re-used for both readable and writable workbooks + * can still make the same method calls on a workbook + */ +public interface WorkbookMethods +{ + /** + * Gets the specified sheet within this workbook + * + * @param index the zero based index of the required sheet + * @return The sheet specified by the index + */ + public Sheet getReadSheet(int index); + + /** + * Gets the name at the specified index + * + * @param index the index into the name table + * @return the name of the cell + */ + public String getName(int index); + + /** + * Gets the index of the name record for the name + * + * @param name the name + * @return the index in the name table + */ + public int getNameIndex(String name); +} Index: 3rdParty_sources/jexcelapi/jxl/biff/WorkspaceInformationRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/WorkspaceInformationRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/WorkspaceInformationRecord.java 17 Aug 2012 14:51:13 -0000 1.1 @@ -0,0 +1,159 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +import common.Logger; +import jxl.read.biff.Record; + +/** + * A record detailing whether the sheet is protected + */ +public class WorkspaceInformationRecord extends WritableRecordData +{ + // the logger + private static Logger logger = + Logger.getLogger(WorkspaceInformationRecord.class); + + /** + * The options byte + */ + private int wsoptions; + + /** + * The row outlines + */ + private boolean rowOutlines; + + /** + * The column outlines + */ + private boolean columnOutlines; + + /** + * The fit to pages flag + */ + private boolean fitToPages; + + // the masks + private static final int FIT_TO_PAGES = 0x100; + private static final int SHOW_ROW_OUTLINE_SYMBOLS = 0x400; + private static final int SHOW_COLUMN_OUTLINE_SYMBOLS = 0x800; + private static final int DEFAULT_OPTIONS = 0x4c1; + + + /** + * Constructs this object from the raw data + * + * @param t the raw data + */ + public WorkspaceInformationRecord(Record t) + { + super(t); + byte[] data = getRecord().getData(); + + wsoptions = IntegerHelper.getInt(data[0], data[1]); + fitToPages = (wsoptions | FIT_TO_PAGES) != 0; + rowOutlines = (wsoptions | SHOW_ROW_OUTLINE_SYMBOLS) != 0; + columnOutlines = (wsoptions | SHOW_COLUMN_OUTLINE_SYMBOLS) != 0; + } + + /** + * Constructs this object from the raw data + */ + public WorkspaceInformationRecord() + { + super(Type.WSBOOL); + wsoptions = DEFAULT_OPTIONS; + } + + /** + * Gets the fit to pages flag + * + * @return TRUE if fit to pages is set + */ + public boolean getFitToPages() + { + return fitToPages; + } + + /** + * Sets the fit to page flag + * + * @param b fit to page indicator + */ + public void setFitToPages(boolean b) + { + fitToPages = b; + } + + /** + * Sets the outlines + */ + public void setRowOutlines(boolean ro) + { + rowOutlines = true; + } + + /** + * Sets the outlines + */ + public void setColumnOutlines(boolean ro) + { + rowOutlines = true; + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + byte[] data = new byte[2]; + + if (fitToPages) + { + wsoptions |= FIT_TO_PAGES; + } + + if (rowOutlines) + { + wsoptions |= SHOW_ROW_OUTLINE_SYMBOLS; + } + + if (columnOutlines) + { + wsoptions |= SHOW_COLUMN_OUTLINE_SYMBOLS; + } + + IntegerHelper.getTwoBytes(wsoptions, data, 0); + + return data; + } +} + + + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/biff/WritableRecordData.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/WritableRecordData.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/WritableRecordData.java 17 Aug 2012 14:51:13 -0000 1.1 @@ -0,0 +1,146 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff; + +import common.Logger; + +import jxl.read.biff.Record; + +/** + * Extension of the standard RecordData which is used to support those + * records which, once read, may also be written + */ +public abstract class WritableRecordData extends RecordData + implements ByteData +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(WritableRecordData.class); + /** + * The maximum length allowed by Excel for any record length + */ + protected static final int maxRecordLength = 8228; + + /** + * Constructor used by the writable records + * + * @param t the biff type of this record + */ + protected WritableRecordData(Type t) + { + super(t); + } + + /** + * Constructor used when reading a record + * + * @param t the raw data read from the biff file + */ + protected WritableRecordData(Record t) + { + super(t); + } + + /** + * Used when writing out records. This portion of the method handles the + * biff code and the length of the record and appends on the data retrieved + * from the subclasses + * + * @return the full record data to be written out to the compound file + */ + public final byte[] getBytes() + { + byte[] data = getData(); + + int dataLength = data.length; + + // Don't the call the automatic continuation code for now + // Assert.verify(dataLength <= maxRecordLength - 4); + // If the bytes length is greater than the max record length + // then split out the data set into continue records + if (data.length > maxRecordLength - 4) + { + dataLength = maxRecordLength - 4; + data = handleContinueRecords(data); + } + + byte[] bytes = new byte[data.length + 4]; + + System.arraycopy(data, 0, bytes, 4, data.length); + + IntegerHelper.getTwoBytes(getCode(), bytes, 0); + IntegerHelper.getTwoBytes(dataLength, bytes, 2); + + return bytes; + } + + /** + * The number of bytes for this record exceeds the maximum record + * length, so a continue is required + * @param data the raw data + * @return the continued data + */ + private byte[] handleContinueRecords(byte[] data) + { + // Deduce the number of continue records + int continuedData = data.length - (maxRecordLength - 4); + int numContinueRecords = continuedData / (maxRecordLength - 4) + 1; + + // Create the new byte array, allowing for the continue records + // code and length + byte[] newdata = new byte[data.length + numContinueRecords * 4]; + + // Copy the bona fide record data into the beginning of the super + // record + System.arraycopy(data, 0, newdata, 0, maxRecordLength - 4); + int oldarraypos = maxRecordLength - 4; + int newarraypos = maxRecordLength - 4; + + // Now handle all the continue records + for (int i = 0; i < numContinueRecords; i++) + { + // The number of bytes to add into the new array + int length = Math.min(data.length - oldarraypos, maxRecordLength - 4); + + // Add in the continue record code + IntegerHelper.getTwoBytes(Type.CONTINUE.value, newdata, newarraypos); + IntegerHelper.getTwoBytes(length, newdata, newarraypos + 2); + + // Copy in as much of the new data as possible + System.arraycopy(data, oldarraypos, newdata, newarraypos + 4, length); + + // Update the position counters + oldarraypos += length; + newarraypos += length + 4; + } + + return newdata; + } + + /** + * Abstract method called by the getBytes method. Subclasses implement + * this method to incorporate their specific binary data - excluding the + * biff code and record length, which is handled by this class + * + * @return subclass specific biff data + */ + protected abstract byte[] getData(); +} Index: 3rdParty_sources/jexcelapi/jxl/biff/XFRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/XFRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/XFRecord.java 17 Aug 2012 14:51:14 -0000 1.1 @@ -0,0 +1,1687 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + + +package jxl.biff; + +import java.text.DateFormat; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.text.NumberFormat; +import java.text.SimpleDateFormat; + +import common.Assert; +import common.Logger; + +import jxl.WorkbookSettings; +import jxl.format.Alignment; +import jxl.format.Border; +import jxl.format.BorderLineStyle; +import jxl.format.CellFormat; +import jxl.format.Colour; +import jxl.format.Font; +import jxl.format.Format; +import jxl.format.Orientation; +import jxl.format.Pattern; +import jxl.format.VerticalAlignment; +import jxl.read.biff.Record; + +/** + * Holds an extended formatting record + */ +public class XFRecord extends WritableRecordData implements CellFormat +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(XFRecord.class); + + /** + * The index to the format record + */ + public int formatIndex; + + /** + * The index of the parent format + */ + private int parentFormat; + + /** + * The format type + */ + private XFType xfFormatType; + + /** + * Indicates whether this is a date formatting record + */ + private boolean date; + + /** + * Indicates whether this is a number formatting record + */ + private boolean number; + + /** + * The date format for this record. Deduced when the record is + * read in from a spreadsheet + */ + private DateFormat dateFormat; + + /** + * The number format for this record. Deduced when the record is read in + * from a spreadsheet + */ + private NumberFormat numberFormat; + + /** + * The used attribute. Needs to be preserved in order to get accurate + * rationalization + */ + private byte usedAttributes; + /** + * The index to the font record used by this XF record + */ + private int fontIndex; + /** + * Flag to indicate whether this XF record represents a locked cell + */ + private boolean locked; + /** + * Flag to indicate whether this XF record is hidden + */ + private boolean hidden; + /** + * The alignment for this cell (left, right, centre) + */ + private Alignment align; + /** + * The vertical alignment for the cell (top, bottom, centre) + */ + private VerticalAlignment valign; + /** + * The orientation of the cell + */ + private Orientation orientation; + /** + * Flag to indicates whether the data (normally text) in the cell will be + * wrapped around to fit in the cell width + */ + private boolean wrap; + + /** + * Indentation of the cell text + */ + private int indentation; + + /** + * Flag to indicate that this format is shrink to fit + */ + private boolean shrinkToFit; + + /** + * The border indicator for the left of this cell + */ + private BorderLineStyle leftBorder; + /** + * The border indicator for the right of the cell + */ + private BorderLineStyle rightBorder; + /** + * The border indicator for the top of the cell + */ + private BorderLineStyle topBorder; + /** + * The border indicator for the bottom of the cell + */ + private BorderLineStyle bottomBorder; + + /** + * The border colour for the left of the cell + */ + private Colour leftBorderColour; + /** + * The border colour for the right of the cell + */ + private Colour rightBorderColour; + /** + * The border colour for the top of the cell + */ + private Colour topBorderColour; + /** + * The border colour for the bottom of the cell + */ + private Colour bottomBorderColour; + + /** + * The background colour + */ + private Colour backgroundColour; + /** + * The background pattern + */ + private Pattern pattern; + /** + * The options mask which is used to store the processed cell options (such + * as alignment, borders etc) + */ + private int options; + /** + * The index of this XF record within the workbook + */ + private int xfIndex; + /** + * The font object for this XF record + */ + private FontRecord font; + /** + * The format object for this XF record. This is used when creating + * a writable record + */ + private DisplayFormat format; + /** + * Flag to indicate whether this XF record has been initialized + */ + private boolean initialized; + + /** + * Indicates whether this cell was constructed by an API or read + * from an existing Excel file + */ + private boolean read; + + /** + * The excel format for this record. This is used to display the actual + * excel format string back to the user (eg. when generating certain + * types of XML document) as opposed to the java equivalent + */ + private Format excelFormat; + + /** + * Flag to indicate whether the format information has been initialized. + * This is false if the xf record has been read in, but true if it + * has been written + */ + private boolean formatInfoInitialized; + + /** + * Flag to indicate whether this cell was copied. If it was copied, then + * it can be set to uninitialized, allowing us to change certain format + * information + */ + private boolean copied; + + /** + * A handle to the formatting records. The purpose of this is + * to read the formatting information back, for the purposes of + * output eg. to some form of XML + */ + private FormattingRecords formattingRecords; + + /** + * Constants for the used attributes + */ + private static final int USE_FONT = 0x4; + private static final int USE_FORMAT = 0x8; + private static final int USE_ALIGNMENT = 0x10; + private static final int USE_BORDER = 0x20; + private static final int USE_BACKGROUND = 0x40; + private static final int USE_PROTECTION = 0x80; + private static final int USE_DEFAULT_VALUE=0xf8; + + /** + * The list of built in date format values + */ + private static final int[] dateFormats = new int[] + {0xe, + 0xf, + 0x10, + 0x11, + 0x12, + 0x13, + 0x14, + 0x15, + 0x16, + 0x2d, + 0x2e, + 0x2f}; + + /** + * The list of java date format equivalents + */ + private static final DateFormat[] javaDateFormats = new DateFormat[] + {SimpleDateFormat.getDateInstance(DateFormat.SHORT), + SimpleDateFormat.getDateInstance(DateFormat.MEDIUM), + new SimpleDateFormat("d-MMM"), + new SimpleDateFormat("MMM-yy"), + new SimpleDateFormat("h:mm a"), + new SimpleDateFormat("h:mm:ss a"), + new SimpleDateFormat("H:mm"), + new SimpleDateFormat("H:mm:ss"), + new SimpleDateFormat("M/d/yy H:mm"), + new SimpleDateFormat("mm:ss"), + new SimpleDateFormat("H:mm:ss"), + new SimpleDateFormat("mm:ss.S")}; + + /** + * The list of built in number format values + */ + private static int[] numberFormats = new int[] + {0x1, + 0x2, + 0x3, + 0x4, + 0x5, + 0x6, + 0x7, + 0x8, + 0x9, + 0xa, + 0xb, + 0x25, + 0x26, + 0x27, + 0x28, + 0x29, + 0x2a, + 0x2b, + 0x2c, + 0x30}; + + /** + * The list of java number format equivalents + */ + private static NumberFormat[] javaNumberFormats = new NumberFormat[] + {new DecimalFormat("0"), + new DecimalFormat("0.00"), + new DecimalFormat("#,##0"), + new DecimalFormat("#,##0.00"), + new DecimalFormat("$#,##0;($#,##0)"), + new DecimalFormat("$#,##0;($#,##0)"), + new DecimalFormat("$#,##0.00;($#,##0.00)"), + new DecimalFormat("$#,##0.00;($#,##0.00)"), + new DecimalFormat("0%"), + new DecimalFormat("0.00%"), + new DecimalFormat("0.00E00"), + new DecimalFormat("#,##0;(#,##0)"), + new DecimalFormat("#,##0;(#,##0)"), + new DecimalFormat("#,##0.00;(#,##0.00)"), + new DecimalFormat("#,##0.00;(#,##0.00)"), + new DecimalFormat("#,##0;(#,##0)"), + new DecimalFormat("$#,##0;($#,##0)"), + new DecimalFormat("#,##0.00;(#,##0.00)"), + new DecimalFormat("$#,##0.00;($#,##0.00)"), + new DecimalFormat("##0.0E0")}; + + // Type to distinguish between biff7 and biff8 + private static class BiffType {}; + + public static final BiffType biff8 = new BiffType(); + public static final BiffType biff7 = new BiffType(); + + /** + * The biff type + */ + private BiffType biffType; + + // Type to distinguish between cell and style records + private static class XFType + { + } + protected static final XFType cell = new XFType(); + protected static final XFType style = new XFType(); + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param bt the biff type + */ + public XFRecord(Record t, WorkbookSettings ws, BiffType bt) + { + super(t); + + biffType = bt; + + byte[] data = getRecord().getData(); + + fontIndex = IntegerHelper.getInt(data[0], data[1]); + formatIndex = IntegerHelper.getInt(data[2], data[3]); + date = false; + number = false; + + + // Compare against the date formats + for (int i = 0; i < dateFormats.length && date == false; i++) + { + if (formatIndex == dateFormats[i]) + { + date = true; + dateFormat = javaDateFormats[i]; + } + } + + // Compare against the number formats + for (int i = 0; i < numberFormats.length && number == false; i++) + { + if (formatIndex == numberFormats[i]) + { + number = true; + DecimalFormat df = (DecimalFormat) javaNumberFormats[i].clone(); + DecimalFormatSymbols symbols = + new DecimalFormatSymbols(ws.getLocale()); + df.setDecimalFormatSymbols(symbols); + numberFormat = df; + //numberFormat = javaNumberFormats[i]; + } + } + + // Initialize the parent format and the type + int cellAttributes = IntegerHelper.getInt(data[4], data[5]); + parentFormat = (cellAttributes & 0xfff0) >> 4; + + int formatType = cellAttributes & 0x4; + xfFormatType = formatType == 0 ? cell : style; + locked = ((cellAttributes & 0x1) != 0); + hidden = ((cellAttributes & 0x2) != 0); + + if (xfFormatType == cell && + (parentFormat & 0xfff) == 0xfff) + { + // Something is screwy with the parent format - set to zero + parentFormat = 0; + logger.warn("Invalid parent format found - ignoring"); + } + + initialized = false; + read = true; + formatInfoInitialized = false; + copied = false; + } + + /** + * A constructor used when creating a writable record + * + * @param fnt the font + * @param form the format + */ + public XFRecord(FontRecord fnt, DisplayFormat form) + { + super(Type.XF); + + initialized = false; + locked = true; + hidden = false; + align = Alignment.GENERAL; + valign = VerticalAlignment.BOTTOM; + orientation = Orientation.HORIZONTAL; + wrap = false; + leftBorder = BorderLineStyle.NONE; + rightBorder = BorderLineStyle.NONE; + topBorder = BorderLineStyle.NONE; + bottomBorder = BorderLineStyle.NONE; + leftBorderColour = Colour.AUTOMATIC; + rightBorderColour = Colour.AUTOMATIC; + topBorderColour = Colour.AUTOMATIC; + bottomBorderColour = Colour.AUTOMATIC; + pattern = Pattern.NONE; + backgroundColour = Colour.DEFAULT_BACKGROUND; + indentation = 0; + shrinkToFit = false; + usedAttributes = (byte) (USE_FONT | USE_FORMAT | + USE_BACKGROUND | USE_ALIGNMENT | USE_BORDER); + + // This will be set by the initialize method and the subclass respectively + parentFormat = 0; + xfFormatType = null; + + font = fnt; + format = form; + biffType = biff8; + read = false; + copied = false; + formatInfoInitialized = true; + + Assert.verify(font != null); + Assert.verify(format != null); + } + + /** + * Copy constructor. Used for copying writable formats, typically + * when duplicating formats to handle merged cells + * + * @param fmt XFRecord + */ + protected XFRecord(XFRecord fmt) + { + super(Type.XF); + + initialized = false; + locked = fmt.locked; + hidden = fmt.hidden; + align = fmt.align; + valign = fmt.valign; + orientation = fmt.orientation; + wrap = fmt.wrap; + leftBorder = fmt.leftBorder; + rightBorder = fmt.rightBorder; + topBorder = fmt.topBorder; + bottomBorder = fmt.bottomBorder; + leftBorderColour = fmt.leftBorderColour; + rightBorderColour = fmt.rightBorderColour; + topBorderColour = fmt.topBorderColour; + bottomBorderColour = fmt.bottomBorderColour; + pattern = fmt.pattern; + xfFormatType = fmt.xfFormatType; + indentation = fmt.indentation; + shrinkToFit = fmt.shrinkToFit; + parentFormat = fmt.parentFormat; + backgroundColour = fmt.backgroundColour; + + // Shallow copy is sufficient for these purposes + font = fmt.font; + format = fmt.format; + + fontIndex = fmt.fontIndex; + formatIndex = fmt.formatIndex; + + formatInfoInitialized = fmt.formatInfoInitialized; + + biffType = biff8; + read = false; + copied = true; + } + + /** + * A public copy constructor which can be used for copy formats between + * different sheets. Unlike the the other copy constructor, this + * version does a deep copy + * + * @param cellFormat the format to copy + */ + protected XFRecord(CellFormat cellFormat) + { + super(Type.XF); + + Assert.verify(cellFormat != null); + Assert.verify(cellFormat instanceof XFRecord); + XFRecord fmt = (XFRecord) cellFormat; + + if (!fmt.formatInfoInitialized) + { + fmt.initializeFormatInformation(); + } + + locked = fmt.locked; + hidden = fmt.hidden; + align = fmt.align; + valign = fmt.valign; + orientation = fmt.orientation; + wrap = fmt.wrap; + leftBorder = fmt.leftBorder; + rightBorder = fmt.rightBorder; + topBorder = fmt.topBorder; + bottomBorder = fmt.bottomBorder; + leftBorderColour = fmt.leftBorderColour; + rightBorderColour = fmt.rightBorderColour; + topBorderColour = fmt.topBorderColour; + bottomBorderColour = fmt.bottomBorderColour; + pattern = fmt.pattern; + xfFormatType = fmt.xfFormatType; + parentFormat = fmt.parentFormat; + indentation = fmt.indentation; + shrinkToFit = fmt.shrinkToFit; + backgroundColour = fmt.backgroundColour; + + // Deep copy of the font + font = new FontRecord(fmt.getFont()); + + // Copy the format + if (fmt.getFormat() == null) + { + // format is writable + if (fmt.format.isBuiltIn()) + { + format = fmt.format; + } + else + { + // Format is not built in, so do a deep copy + format = new FormatRecord((FormatRecord) fmt.format); + } + } + else if (fmt.getFormat() instanceof BuiltInFormat) + { + // read excel format is built in + excelFormat = (BuiltInFormat) fmt.excelFormat; + format = (BuiltInFormat) fmt.excelFormat; + } + else + { + // read excel format is user defined + Assert.verify(fmt.formatInfoInitialized); + + // in this case FormattingRecords should initialize the excelFormat + // field with an instance of FormatRecord + Assert.verify(fmt.excelFormat instanceof FormatRecord); + + // Format is not built in, so do a deep copy + FormatRecord fr = new FormatRecord((FormatRecord) fmt.excelFormat); + + // Set both format fields to be the same object, since + // FormatRecord implements all the necessary interfaces + excelFormat = fr; + format = fr; + } + + biffType = biff8; + + // The format info should be all OK by virtue of the deep copy + formatInfoInitialized = true; + + // This format was not read in + read = false; + + // Treat this as a new cell record, so set the copied flag to false + copied = false; + + // The font or format indexes need to be set, so set initialized to false + initialized = false; + } + + /** + * Gets the java date format for this format record + * + * @return returns the date format + */ + public DateFormat getDateFormat() + { + return dateFormat; + } + + /** + * Gets the java number format for this format record + * + * @return returns the number format + */ + public NumberFormat getNumberFormat() + { + return numberFormat; + } + + /** + * Gets the lookup number of the format record + * + * @return returns the lookup number of the format record + */ + public int getFormatRecord() + { + return formatIndex; + } + + /** + * Sees if this format is a date format + * + * @return TRUE if this refers to a built in date format + */ + public boolean isDate() + { + return date; + } + + /** + * Sees if this format is a number format + * + * @return TRUE if this refers to a built in date format + */ + public boolean isNumber() + { + return number; + } + + /** + * Converts the various fields into binary data. If this object has + * been read from an Excel file rather than being requested by a user (ie. + * if the read flag is TRUE) then + * no processing takes place and the raw data is simply returned. + * + * @return the raw data for writing + */ + public byte[] getData() + { + // Format rationalization process means that we always want to + // regenerate the format info - even if the spreadsheet was + // read in + if (!formatInfoInitialized) + { + initializeFormatInformation(); + } + + byte[] data = new byte[20]; + + IntegerHelper.getTwoBytes(fontIndex, data, 0); + IntegerHelper.getTwoBytes(formatIndex, data, 2); + + // Do the cell attributes + int cellAttributes = 0; + + if (getLocked()) + { + cellAttributes |= 0x01; + } + + if (getHidden()) + { + cellAttributes |= 0x02; + } + + if (xfFormatType == style) + { + cellAttributes |= 0x04; + parentFormat = 0xffff; + } + + cellAttributes |= (parentFormat << 4); + + IntegerHelper.getTwoBytes(cellAttributes, data, 4); + + int alignMask = align.getValue(); + + if (wrap) + { + alignMask |= 0x08; + } + + alignMask |= (valign.getValue() << 4); + + alignMask |= (orientation.getValue() << 8); + + IntegerHelper.getTwoBytes(alignMask, data, 6); + + data[9] = (byte) 0x10; + + // Set the borders + int borderMask = leftBorder.getValue(); + borderMask |= (rightBorder.getValue() << 4); + borderMask |= (topBorder.getValue() << 8); + borderMask |= (bottomBorder.getValue() << 12); + + IntegerHelper.getTwoBytes(borderMask, data, 10); + + // Set the border palette information if border mask is non zero + // Hard code the colours to be black + if (borderMask != 0) + { + byte lc = (byte)leftBorderColour.getValue(); + byte rc = (byte)rightBorderColour.getValue(); + byte tc = (byte)topBorderColour.getValue(); + byte bc = (byte)bottomBorderColour.getValue(); + + int sideColourMask = (lc & 0x7f) | ((rc & 0x7f) << 7); + int topColourMask = (tc & 0x7f) | ((bc & 0x7f) << 7); + + IntegerHelper.getTwoBytes(sideColourMask, data, 12); + IntegerHelper.getTwoBytes(topColourMask, data, 14); + } + + // Set the background pattern + int patternVal = pattern.getValue() << 10; + IntegerHelper.getTwoBytes(patternVal, data, 16); + + // Set the colour palette + int colourPaletteMask = backgroundColour.getValue(); + colourPaletteMask |= (0x40 << 7); + IntegerHelper.getTwoBytes(colourPaletteMask, data, 18); + + // Set the cell options + options |= indentation & 0x0f; + + if (shrinkToFit) + { + options |= 0x10; + } + else + { + options &= 0xef; + } + + data[8] = (byte) options; + + if (biffType == biff8) + { + data[9] = (byte) usedAttributes; + } + + return data; + } + + /** + * Accessor for the locked flag + * + * @return TRUE if this XF record locks cells, FALSE otherwise + */ + protected final boolean getLocked() + { + return locked; + } + + /** + * Accessor for the hidden flag + * + * @return TRUE if this XF record hides the cell, FALSE otherwise + */ + protected final boolean getHidden() + { + return hidden; + } + + /** + * Sets whether or not this XF record locks the cell + * + * @param l the locked flag + */ + protected final void setXFLocked(boolean l) + { + locked = l; + usedAttributes |= USE_PROTECTION; + } + + /** + * Sets the cell options + * + * @param opt the cell options + */ + protected final void setXFCellOptions(int opt) + { + options |= opt; + } + + /** + * Sets the horizontal alignment for the data in this cell. + * This method should only be called from its writable subclass + * CellXFRecord + * + * @param a the alignment + */ + protected void setXFAlignment(Alignment a) + { + Assert.verify(!initialized); + align = a; + usedAttributes |= USE_ALIGNMENT; + } + + /** + * Sets the indentation + * + * @param i the indentation + */ + protected void setXFIndentation(int i) + { + Assert.verify(!initialized); + indentation = i; + usedAttributes |= USE_ALIGNMENT; + } + + /** + * Sets the shrink to fit flag + * + * @param s the shrink to fit flag + */ + protected void setXFShrinkToFit(boolean s) + { + Assert.verify(!initialized); + shrinkToFit = s; + usedAttributes |= USE_ALIGNMENT; + } + + /** + * Gets the horizontal cell alignment + * + * @return the alignment + */ + public Alignment getAlignment() + { + if (!formatInfoInitialized) + { + initializeFormatInformation(); + } + + return align; + } + + /** + * Gets the indentation + * + * @return the indentation + */ + public int getIndentation() + { + if (!formatInfoInitialized) + { + initializeFormatInformation(); + } + + return indentation; + } + + /** + * Gets the shrink to fit flag + * + * @return TRUE if this format is shrink to fit, FALSE otherise + */ + public boolean isShrinkToFit() + { + if (!formatInfoInitialized) + { + initializeFormatInformation(); + } + + return shrinkToFit; + } + + /** + * Accessor for whether a particular cell is locked + * + * @return TRUE if this cell is locked, FALSE otherwise + */ + public boolean isLocked() + { + if (!formatInfoInitialized) + { + initializeFormatInformation(); + } + + return locked; + } + + + /** + * Gets the vertical cell alignment + * + * @return the alignment + */ + public VerticalAlignment getVerticalAlignment() + { + if (!formatInfoInitialized) + { + initializeFormatInformation(); + } + + return valign; + } + + /** + * Gets the orientation + * + * @return the orientation + */ + public Orientation getOrientation() + { + if (!formatInfoInitialized) + { + initializeFormatInformation(); + } + + return orientation; + } + + /** + * Sets the horizontal alignment for the data in this cell. + * This method should only be called from its writable subclass + * CellXFRecord + * + * @param c the background colour + * @param p the background pattern + */ + protected void setXFBackground(Colour c, Pattern p) + { + Assert.verify(!initialized); + backgroundColour = c; + pattern = p; + usedAttributes |= USE_BACKGROUND; + } + + /** + * Gets the background colour used by this cell + * + * @return the foreground colour + */ + public Colour getBackgroundColour() + { + if (!formatInfoInitialized) + { + initializeFormatInformation(); + } + + return backgroundColour; + } + + /** + * Gets the pattern used by this cell format + * + * @return the background pattern + */ + public Pattern getPattern() + { + if (!formatInfoInitialized) + { + initializeFormatInformation(); + } + + return pattern; + } + + /** + * Sets the vertical alignment for the data in this cell + * This method should only be called from its writable subclass + * CellXFRecord + + * + * @param va the vertical alignment + */ + protected void setXFVerticalAlignment(VerticalAlignment va) + { + Assert.verify(!initialized); + valign = va; + usedAttributes |= USE_ALIGNMENT; + } + + /** + * Sets the vertical alignment for the data in this cell + * This method should only be called from its writable subclass + * CellXFRecord + + * + * @param o the orientation + */ + protected void setXFOrientation(Orientation o) + { + Assert.verify(!initialized); + orientation = o; + usedAttributes |= USE_ALIGNMENT; + } + + /** + * Sets whether the data in this cell is wrapped + * This method should only be called from its writable subclass + * CellXFRecord + * + * @param w the wrap flag + */ + protected void setXFWrap(boolean w) + { + Assert.verify(!initialized); + wrap = w; + usedAttributes |= USE_ALIGNMENT; + } + + /** + * Gets whether or not the contents of this cell are wrapped + * + * @return TRUE if this cell's contents are wrapped, FALSE otherwise + */ + public boolean getWrap() + { + if (!formatInfoInitialized) + { + initializeFormatInformation(); + } + + return wrap; + } + + /** + * Sets the border for this cell + * This method should only be called from its writable subclass + * CellXFRecord + * + * @param b the border + * @param ls the border line style + */ + protected void setXFBorder(Border b, BorderLineStyle ls, Colour c) + { + Assert.verify(!initialized); + + if (c == Colour.BLACK || c == Colour.UNKNOWN) + { + c = Colour.PALETTE_BLACK; + } + + if (b == Border.LEFT) + { + leftBorder = ls; + leftBorderColour = c; + } + else if (b == Border.RIGHT) + { + rightBorder = ls; + rightBorderColour = c; + } + else if (b == Border.TOP) + { + topBorder = ls; + topBorderColour = c; + } + else if (b == Border.BOTTOM) + { + bottomBorder = ls; + bottomBorderColour = c; + } + + usedAttributes |= USE_BORDER; + + return; + } + + + /** + * Gets the line style for the given cell border + * If a border type of ALL or NONE is specified, then a line style of + * NONE is returned + * + * @param border the cell border we are interested in + * @return the line style of the specified border + */ + public BorderLineStyle getBorder(Border border) + { + return getBorderLine(border); + } + + /** + * Gets the line style for the given cell border + * If a border type of ALL or NONE is specified, then a line style of + * NONE is returned + * + * @param border the cell border we are interested in + * @return the line style of the specified border + */ + public BorderLineStyle getBorderLine(Border border) + { + // Don't bother with the short cut records + if (border == Border.NONE || + border == Border.ALL) + { + return BorderLineStyle.NONE; + } + + if (!formatInfoInitialized) + { + initializeFormatInformation(); + } + + if (border == Border.LEFT) + { + return leftBorder; + } + else if (border == Border.RIGHT) + { + return rightBorder; + } + else if (border == Border.TOP) + { + return topBorder; + } + else if (border == Border.BOTTOM) + { + return bottomBorder; + } + + return BorderLineStyle.NONE; + } + + /** + * Gets the line style for the given cell border + * If a border type of ALL or NONE is specified, then a line style of + * NONE is returned + * + * @param border the cell border we are interested in + * @return the line style of the specified border + */ + public Colour getBorderColour(Border border) + { + // Don't bother with the short cut records + if (border == Border.NONE || + border == Border.ALL) + { + return Colour.PALETTE_BLACK; + } + + if (!formatInfoInitialized) + { + initializeFormatInformation(); + } + + if (border == Border.LEFT) + { + return leftBorderColour; + } + else if (border == Border.RIGHT) + { + return rightBorderColour; + } + else if (border == Border.TOP) + { + return topBorderColour; + } + else if (border == Border.BOTTOM) + { + return bottomBorderColour; + } + + return Colour.BLACK; + } + + + /** + * Determines if this cell format has any borders at all. Used to + * set the new borders when merging a group of cells + * + * @return TRUE if this cell has any borders, FALSE otherwise + */ + public final boolean hasBorders() + { + if (!formatInfoInitialized) + { + initializeFormatInformation(); + } + + if (leftBorder == BorderLineStyle.NONE && + rightBorder == BorderLineStyle.NONE && + topBorder == BorderLineStyle.NONE && + bottomBorder == BorderLineStyle.NONE) + { + return false; + } + + return true; + } + + /** + * If this cell has not been read in from an existing Excel sheet, + * then initializes this record with the XF index passed in. Calls + * initialized on the font and format record + * + * @param pos the xf index to initialize this record with + * @param fr the containing formatting records + * @param fonts the container for the fonts + * @exception NumFormatRecordsException + */ + public final void initialize(int pos, FormattingRecords fr, Fonts fonts) + throws NumFormatRecordsException + { + xfIndex = pos; + formattingRecords = fr; + + // If this file has been read in or copied, + // the font and format indexes will + // already be initialized, so just set the initialized flag and + // return + if (read || copied) + { + initialized = true; + return; + } + + if (!font.isInitialized()) + { + fonts.addFont(font); + } + + if (!format.isInitialized()) + { + fr.addFormat(format); + } + + fontIndex = font.getFontIndex(); + formatIndex = format.getFormatIndex(); + + initialized = true; + } + + /** + * Resets the initialize flag. This is called by the constructor of + * WritableWorkbookImpl to reset the statically declared fonts + */ + public final void uninitialize() + { + // As the default formats are cloned internally, the initialized + // flag should never be anything other than false + if (initialized == true) + { + logger.warn("A default format has been initialized"); + } + initialized = false; + } + + /** + * Sets the XF index. Called when rationalizing the XF records + * immediately prior to writing + * + * @param xfi the new xf index + */ + final void setXFIndex(int xfi) + { + xfIndex = xfi; + } + + /** + * Accessor for the XF index + * + * @return the XF index for this cell + */ + public final int getXFIndex() + { + return xfIndex; + } + + /** + * Accessor to see if this format is initialized + * + * @return TRUE if this format is initialized, FALSE otherwise + */ + public final boolean isInitialized() + { + return initialized; + } + + /** + * Accessor to see if this format was read in. Used when checking merged + * cells + * + * @return TRUE if this XF record was read in, FALSE if it was generated by + * the user API + */ + public final boolean isRead() + { + return read; + } + + /** + * Gets the format used by this format + * + * @return the format + */ + public Format getFormat() + { + if (!formatInfoInitialized) + { + initializeFormatInformation(); + } + return excelFormat; + } + + /** + * Gets the font used by this format + * + * @return the font + */ + public Font getFont() + { + if (!formatInfoInitialized) + { + initializeFormatInformation(); + } + return font; + } + + /** + * Initializes the internal format information from the data read in + */ + private void initializeFormatInformation() + { + // Initialize the cell format string + if (formatIndex < BuiltInFormat.builtIns.length && + BuiltInFormat.builtIns[formatIndex] != null) + { + excelFormat = BuiltInFormat.builtIns[formatIndex]; + } + else + { + excelFormat = formattingRecords.getFormatRecord(formatIndex); + } + + // Initialize the font + font = formattingRecords.getFonts().getFont(fontIndex); + + // Initialize the cell format data from the binary record + byte[] data = getRecord().getData(); + + // Get the parent record + int cellAttributes = IntegerHelper.getInt(data[4], data[5]); + parentFormat = (cellAttributes & 0xfff0) >> 4; + int formatType = cellAttributes & 0x4; + xfFormatType = formatType == 0 ? cell : style; + locked = ((cellAttributes & 0x1) != 0); + hidden = ((cellAttributes & 0x2) != 0); + + if (xfFormatType == cell && + (parentFormat & 0xfff) == 0xfff) + { + // Something is screwy with the parent format - set to zero + parentFormat = 0; + logger.warn("Invalid parent format found - ignoring"); + } + + + int alignMask = IntegerHelper.getInt(data[6], data[7]); + + // Get the wrap + if ((alignMask & 0x08) != 0) + { + wrap = true; + } + + // Get the horizontal alignment + align = Alignment.getAlignment(alignMask & 0x7); + + // Get the vertical alignment + valign = VerticalAlignment.getAlignment((alignMask >> 4) & 0x7); + + // Get the orientation + orientation = Orientation.getOrientation((alignMask >> 8) & 0xff); + + int attr = IntegerHelper.getInt(data[8], data[9]); + + // Get the indentation + indentation = attr & 0x0F; + + // Get the shrink to fit flag + shrinkToFit = (attr & 0x10) != 0; + + // Get the used attribute + if (biffType == biff8) + { + usedAttributes = data[9]; + } + + // Get the borders + int borderMask = IntegerHelper.getInt(data[10], data[11]); + + leftBorder = BorderLineStyle.getStyle(borderMask & 0x7); + rightBorder = BorderLineStyle.getStyle((borderMask >> 4) & 0x7); + topBorder = BorderLineStyle.getStyle((borderMask >> 8) & 0x7); + bottomBorder = BorderLineStyle.getStyle((borderMask >> 12) & 0x7); + + int borderColourMask = IntegerHelper.getInt(data[12], data[13]); + + leftBorderColour = Colour.getInternalColour(borderColourMask & 0x7f); + rightBorderColour = Colour.getInternalColour + ((borderColourMask & 0x3f80) >> 7); + + borderColourMask = IntegerHelper.getInt(data[14], data[15]); + topBorderColour = Colour.getInternalColour(borderColourMask & 0x7f); + bottomBorderColour = Colour.getInternalColour + ((borderColourMask & 0x3f80) >> 7); + + if (biffType == biff8) + { + // Get the background pattern. This is the six most significant bits + int patternVal = IntegerHelper.getInt(data[16], data[17]); + patternVal = patternVal & 0xfc00; + patternVal = patternVal >> 10; + pattern = Pattern.getPattern(patternVal); + + // Get the background colour + int colourPaletteMask = IntegerHelper.getInt(data[18], data[19]); + backgroundColour = Colour.getInternalColour(colourPaletteMask & 0x3f); + + if (backgroundColour == Colour.UNKNOWN || + backgroundColour == Colour.DEFAULT_BACKGROUND1) + { + backgroundColour = Colour.DEFAULT_BACKGROUND; + } + } + else + { + pattern = Pattern.NONE; + backgroundColour = Colour.DEFAULT_BACKGROUND; + } + + // Set the lazy initialization flag + formatInfoInitialized = true; + } + + /** + * Standard hash code implementation + * @return the hash code + */ + public int hashCode() + { + // Must have its formats info initialized in order to compute the hash code + if (!formatInfoInitialized) + { + initializeFormatInformation(); + } + + int hashValue = 17; + int oddPrimeNumber = 37; + + // The boolean fields + hashValue = oddPrimeNumber*hashValue + (hidden ? 1:0); + hashValue = oddPrimeNumber*hashValue + (locked ? 1:0); + hashValue = oddPrimeNumber*hashValue + (wrap ? 1:0); + hashValue = oddPrimeNumber*hashValue + (shrinkToFit ? 1:0); + + // The enumerations + if (xfFormatType == cell) + { + hashValue = oddPrimeNumber*hashValue + 1; + } + else if (xfFormatType == style) + { + hashValue = oddPrimeNumber*hashValue + 2; + } + + hashValue = oddPrimeNumber*hashValue + (align.getValue() + 1); + hashValue = oddPrimeNumber*hashValue + (valign.getValue() + 1); + hashValue = oddPrimeNumber*hashValue + (orientation.getValue()); + + hashValue ^= leftBorder.getDescription().hashCode(); + hashValue ^= rightBorder.getDescription().hashCode(); + hashValue ^= topBorder.getDescription().hashCode(); + hashValue ^= bottomBorder.getDescription().hashCode(); + + hashValue = oddPrimeNumber*hashValue + (leftBorderColour.getValue()); + hashValue = oddPrimeNumber*hashValue + (rightBorderColour.getValue()); + hashValue = oddPrimeNumber*hashValue + (topBorderColour.getValue()); + hashValue = oddPrimeNumber*hashValue + (bottomBorderColour.getValue()); + hashValue = oddPrimeNumber*hashValue + (backgroundColour.getValue()); + hashValue = oddPrimeNumber*hashValue + (pattern.getValue() + 1); + + // The integer fields + hashValue = oddPrimeNumber*hashValue + usedAttributes; + hashValue = oddPrimeNumber*hashValue + parentFormat; + hashValue = oddPrimeNumber*hashValue + fontIndex; + hashValue = oddPrimeNumber*hashValue + formatIndex; + hashValue = oddPrimeNumber*hashValue + indentation; + + return hashValue; + } + + /** + * Equals method. This is called when comparing writable formats + * in order to prevent duplicate formats being added to the workbook + * + * @param o object to compare + * @return TRUE if the objects are equal, FALSE otherwise + */ + public boolean equals(Object o) + { + if (o == this) + { + return true; + } + + if (!(o instanceof XFRecord)) + { + return false; + } + + XFRecord xfr = (XFRecord) o; + + // Both records must be writable and have their format info initialized + if (!formatInfoInitialized) + { + initializeFormatInformation(); + } + + if (!xfr.formatInfoInitialized) + { + xfr.initializeFormatInformation(); + } + + if (xfFormatType != xfr.xfFormatType || + parentFormat != xfr.parentFormat || + locked != xfr.locked || + hidden != xfr.hidden || + usedAttributes != xfr.usedAttributes) + { + return false; + } + + if (align != xfr.align || + valign != xfr.valign || + orientation != xfr.orientation || + wrap != xfr.wrap || + shrinkToFit != xfr.shrinkToFit || + indentation != xfr.indentation) + { + return false; + } + + if (leftBorder != xfr.leftBorder || + rightBorder != xfr.rightBorder || + topBorder != xfr.topBorder || + bottomBorder != xfr.bottomBorder) + { + return false; + } + + if (leftBorderColour != xfr.leftBorderColour || + rightBorderColour != xfr.rightBorderColour || + topBorderColour != xfr.topBorderColour || + bottomBorderColour != xfr.bottomBorderColour) + { + return false; + } + + if (backgroundColour != xfr.backgroundColour || + pattern != xfr.pattern) + { + return false; + } + + if (initialized && xfr.initialized) + { + // Both formats are initialized, so it is sufficient to just do + // shallow equals on font, format objects, + // since we are testing for the presence of clones anwyay + // Use indices rather than objects because of the rationalization + // process (which does not set the object on an XFRecord) + if (fontIndex != xfr.fontIndex || + formatIndex != xfr.formatIndex) + { + return false; + } + } + else + { + // Perform a deep compare of fonts and formats + if (!font.equals(xfr.font) || + !format.equals(xfr.format)) + { + return false; + } + } + + return true; + } + + /** + * Sets the format index. This is called during the rationalization process + * when some of the duplicate number formats have been removed + * @param newindex the new format index + */ + void setFormatIndex(int newindex) + { + formatIndex = newindex; + } + + /** + * Accessor for the font index. Called by the FormattingRecords objects + * during the rationalization process + * @return the font index + */ + public int getFontIndex() + { + return fontIndex; + } + + + /** + * Sets the font index. This is called during the rationalization process + * when some of the duplicate fonts have been removed + * @param newindex the new index + */ + void setFontIndex(int newindex) + { + fontIndex = newindex; + } + + /** + * Sets the format type and parent format from the writable subclass + * @param t the xf type + * @param pf the parent format + */ + protected void setXFDetails(XFType t, int pf) + { + xfFormatType = t; + parentFormat = pf; + } + + /** + * Changes the appropriate indexes during the rationalization process + * @param xfMapping the xf index re-mappings + */ + void rationalize(IndexMapping xfMapping) + { + xfIndex = xfMapping.getNewIndex(xfIndex); + + if (xfFormatType == cell) + { + parentFormat = xfMapping.getNewIndex(parentFormat); + } + } + + /** + * Sets the font object with a workbook specific clone. Called from + * the CellValue object when the font has been identified as a statically + * shared font + * Also called to superimpose a HyperlinkFont on an existing label cell + */ + public void setFont(FontRecord f) + { + // This style cannot be initialized, otherwise it would mean it would + // have been initialized with shared font + // However, sometimes (when setting a row or column format) an initialized + // XFRecord may have its font overridden by the column/row + + font = f; + } +} + Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/BStoreContainer.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/BStoreContainer.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/BStoreContainer.java 17 Aug 2012 14:51:19 -0000 1.1 @@ -0,0 +1,92 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +import common.Logger; + +/** + * A BStoreContainer escher record + */ +class BStoreContainer extends EscherContainer +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(BStoreContainer.class); + + /** + * The number of blips inside this container + */ + private int numBlips; + + /** + * Constructor used to instantiate this object when reading from an + * escher stream + * + * @param erd the escher data + */ + public BStoreContainer(EscherRecordData erd) + { + super(erd); + numBlips = getInstance(); + } + + /** + * Constructor used when writing out an escher record + */ + public BStoreContainer() + { + super(EscherRecordType.BSTORE_CONTAINER); + } + + /** + * Sets the number of drawings in this container + * + * @param count the number of blips + */ + void setNumBlips(int count) + { + numBlips = count; + setInstance(numBlips); + } + + /** + * Accessor for the number of blips + * + * @return the number of blips + */ + public int getNumBlips() + { + return numBlips; + } + + /** + * Accessor for the drawing + * + * @param i the index number of the drawing to return + * @return the drawing + */ + public BlipStoreEntry getDrawing(int i) + { + EscherRecord[] children = getChildren(); + BlipStoreEntry bse = (BlipStoreEntry) children[i]; + return bse; + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/BlipStoreEntry.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/BlipStoreEntry.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/BlipStoreEntry.java 17 Aug 2012 14:51:19 -0000 1.1 @@ -0,0 +1,214 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +import java.io.IOException; + +import common.Assert; +import common.Logger; + +import jxl.biff.IntegerHelper; + +/** + * The data for this blip store entry. Typically this is the raw image data + */ +class BlipStoreEntry extends EscherAtom +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(BlipStoreEntry.class); + + /** + * The type of the blip + */ + private BlipType type; + + /** + * The image data read in + */ + private byte[] data; + + /** + * The length of the image data + */ + private int imageDataLength; + + /** + * The reference count on this blip + */ + private int referenceCount; + + /** + * Flag to indicate that this entry was specified by the API, and not + * read in + */ + private boolean write; + + /** + * The start of the image data within this blip entry + */ + private static final int IMAGE_DATA_OFFSET = 61; + + /** + * Constructor + * + * @param erd the escher record data + */ + public BlipStoreEntry(EscherRecordData erd) + { + super(erd); + type = BlipType.getType(getInstance()); + write = false; + byte[] bytes = getBytes(); + referenceCount = IntegerHelper.getInt(bytes[24], bytes[25], + bytes[26], bytes[27]); + } + + /** + * Constructor + * + * @param d the drawing + * @exception IOException + */ + public BlipStoreEntry(Drawing d) throws IOException + { + super(EscherRecordType.BSE); + type = BlipType.PNG; + setVersion(2); + setInstance(type.getValue()); + + byte[] imageData = d.getImageBytes(); + imageDataLength = imageData.length; + data = new byte[imageDataLength + IMAGE_DATA_OFFSET]; + System.arraycopy(imageData, 0, data, IMAGE_DATA_OFFSET, imageDataLength); + referenceCount = d.getReferenceCount(); + write = true; + } + + /** + * Accessor for the blip type + * + * @return the blip type + */ + public BlipType getBlipType() + { + return type; + } + + /** + * Gets the data for this blip so that it can be written out + * + * @return the data for the blip + */ + public byte[] getData() + { + if (write) + { + // Drawing has been specified by API + + // Type on win32 + data[0] = (byte) type.getValue(); + + // Type on MacOs + data[1] = (byte) type.getValue(); + + // The blip identifier + // IntegerHelper.getTwoBytes(0xfce1, data, 2); + + // Unused tags - 18 bytes + // System.arraycopy(stuff, 0, data, 2, stuff.length); + + // The size of the file + IntegerHelper.getFourBytes(imageDataLength + 8 + 17, data, 20); + + // The reference count on the blip + IntegerHelper.getFourBytes(referenceCount, data, 24); + + // Offset in the delay stream + IntegerHelper.getFourBytes(0, data, 28); + + // Usage byte + data[32] = (byte) 0; + + // Length of the blip name + data[33] = (byte) 0; + + // Last two bytes unused + data[34] = (byte) 0x7e; + data[35] = (byte) 0x01; + + // The blip itself + data[36] = (byte) 0; + data[37] = (byte) 0x6e; + + // The blip identifier + IntegerHelper.getTwoBytes(0xf01e, data, 38); + + // The length of the blip. This is the length of the image file plus + // 16 bytes + IntegerHelper.getFourBytes(imageDataLength + 17, data, 40); + + // Unknown stuff + // System.arraycopy(stuff, 0, data, 44, stuff.length); + } + else + { + // drawing has been read in + data = getBytes(); + } + + return setHeaderData(data); + } + + /** + * Reduces the reference count in this blip. Called when a drawing is + * removed + */ + void dereference() + { + referenceCount--; + Assert.verify(referenceCount >= 0); + } + + /** + * Accessor for the reference count on the blip + * + * @return the reference count on the blip + */ + int getReferenceCount() + { + return referenceCount; + } + + /** + * Accessor for the image data. + * + * @return the image data + */ + byte[] getImageData() + { + byte[] allData = getBytes(); + byte[] imageData = new byte[allData.length - IMAGE_DATA_OFFSET]; + System.arraycopy(allData, IMAGE_DATA_OFFSET, + imageData, 0, imageData.length); + return imageData; + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/BlipType.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/BlipType.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/BlipType.java 17 Aug 2012 14:51:20 -0000 1.1 @@ -0,0 +1,117 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +/** + * Enumeration for the BLIP type + */ +final class BlipType +{ + /** + * The blip type code + */ + private int value; + + /** + * The blip type description + */ + private String desc; + + /** + * All blip types + */ + private static BlipType[] types = new BlipType[0]; + + /** + * Constructor + * + * @param val the code + * @param d the description + */ + private BlipType(int val, String d) + { + value = val; + desc = d; + + BlipType[] newtypes = new BlipType[types.length + 1]; + System.arraycopy(types, 0, newtypes, 0, types.length); + newtypes[types.length] = this; + types = newtypes; + } + + /** + * Accessor for the description + * + * @return the description + */ + public String getDescription() + { + return desc; + } + + /** + * Accessor for the value + * + * @return the value + */ + public int getValue() + { + return value; + } + + /** + * Gets the blip type given the value + * + * @param val get the value + * @return the blip type + */ + public static BlipType getType(int val) + { + BlipType type = UNKNOWN; + for (int i = 0; i < types.length; i++) + { + if (types[i].value == val) + { + type = types[i]; + break; + } + } + + return type; + } + + public static final BlipType ERROR = new BlipType(0, "Error"); + // An error occured during loading + public static final BlipType UNKNOWN = new BlipType(1, "Unknown"); + // An unknown blip type + public static final BlipType EMF = new BlipType(2, "EMF"); + // Windows Enhanced Metafile + public static final BlipType WMF = new BlipType(3, "WMF"); + // Windows Metafile + public static final BlipType PICT = new BlipType(4, "PICT"); + // Macintosh PICT + public static final BlipType JPEG = new BlipType(5, "JPEG"); // JFIF + public static final BlipType PNG = new BlipType(6, "PNG"); // PNG + public static final BlipType DIB = new BlipType(7, "DIB"); // Windows DIB + public static final BlipType FIRST_CLIENT = new BlipType(32, "FIRST"); + // First client defined blip type + public static final BlipType LAST_CLIENT = new BlipType(255, "LAST"); + // Last client defined blip type +} Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/Button.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/Button.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/Button.java 17 Aug 2012 14:51:20 -0000 1.1 @@ -0,0 +1,867 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +import java.io.IOException; + +import common.Assert; +import common.Logger; + +import jxl.WorkbookSettings; +import jxl.biff.ContinueRecord; +import jxl.biff.IntegerHelper; +import jxl.biff.StringHelper; +import jxl.write.biff.File; + +/** + * Contains the various biff records used to copy a Button (from the + * Form toolbox) between workbook + */ +public class Button implements DrawingGroupObject +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(Button.class); + + /** + * The spContainer that was read in + */ + private EscherContainer readSpContainer; + + /** + * The spContainer that was generated + */ + private EscherContainer spContainer; + + /** + * The MsoDrawingRecord associated with the drawing + */ + private MsoDrawingRecord msoDrawingRecord; + + /** + * The ObjRecord associated with the drawing + */ + private ObjRecord objRecord; + + /** + * Initialized flag + */ + private boolean initialized = false; + + /** + * The object id, assigned by the drawing group + */ + private int objectId; + + /** + * The blip id + */ + private int blipId; + + /** + * The shape id + */ + private int shapeId; + + /** + * The column + */ + private int column; + + /** + * The row position of the image + */ + private int row; + + /** + * The width of the image in cells + */ + private double width; + + /** + * The height of the image in cells + */ + private double height; + + /** + * The number of places this drawing is referenced + */ + private int referenceCount; + + /** + * The top level escher container + */ + private EscherContainer escherData; + + /** + * Where this image came from (read, written or a copy) + */ + private Origin origin; + + /** + * The drawing group for all the images + */ + private DrawingGroup drawingGroup; + + /** + * The drawing data + */ + private DrawingData drawingData; + + /** + * The type of this drawing object + */ + private ShapeType type; + + /** + * The drawing position on the sheet + */ + private int drawingNumber; + + /** + * An mso drawing record, which sometimes appears + */ + private MsoDrawingRecord mso; + + /** + * The text object record + */ + private TextObjectRecord txo; + + /** + * Text data from the first continue record + */ + private ContinueRecord text; + + /** + * Formatting data from the second continue record + */ + private ContinueRecord formatting; + + /** + * The comment text + */ + private String commentText; + + /** + * The workbook settings + */ + private WorkbookSettings workbookSettings; + + /** + * Constructor used when reading images + * + * @param msodr the drawing record + * @param obj the object record + * @param dd the drawing data for all drawings on this sheet + * @param dg the drawing group + * @param ws the workbook settings + */ + public Button(MsoDrawingRecord msodr, + ObjRecord obj, + DrawingData dd, + DrawingGroup dg, + WorkbookSettings ws) + { + drawingGroup = dg; + msoDrawingRecord = msodr; + drawingData = dd; + objRecord = obj; + initialized = false; + workbookSettings = ws; + origin = Origin.READ; + drawingData.addData(msoDrawingRecord.getData()); + drawingNumber = drawingData.getNumDrawings() - 1; + drawingGroup.addDrawing(this); + + Assert.verify(msoDrawingRecord != null && objRecord != null); + + initialize(); + } + + /** + * Copy constructor used to copy drawings from read to write + * + * @param dgo the drawing group object + * @param dg the drawing group + * @param ws the workbook settings + */ + public Button(DrawingGroupObject dgo, + DrawingGroup dg, + WorkbookSettings ws) + { + Button d = (Button) dgo; + Assert.verify(d.origin == Origin.READ); + msoDrawingRecord = d.msoDrawingRecord; + objRecord = d.objRecord; + initialized = false; + origin = Origin.READ; + drawingData = d.drawingData; + drawingGroup = dg; + drawingNumber = d.drawingNumber; + drawingGroup.addDrawing(this); + mso = d.mso; + txo = d.txo; + text = d.text; + formatting = d.formatting; + workbookSettings = ws; + } + + /** + * Initializes the member variables from the Escher stream data + */ + private void initialize() + { + readSpContainer = drawingData.getSpContainer(drawingNumber); + Assert.verify(readSpContainer != null); + + EscherRecord[] children = readSpContainer.getChildren(); + + Sp sp = (Sp) readSpContainer.getChildren()[0]; + objectId = objRecord.getObjectId(); + shapeId = sp.getShapeId(); + type = ShapeType.getType(sp.getShapeType()); + + if (type == ShapeType.UNKNOWN) + { + logger.warn("Unknown shape type"); + } + + ClientAnchor clientAnchor = null; + for (int i = 0; i < children.length && clientAnchor == null; i++) + { + if (children[i].getType() == EscherRecordType.CLIENT_ANCHOR) + { + clientAnchor = (ClientAnchor) children[i]; + } + } + + if (clientAnchor == null) + { + logger.warn("Client anchor not found"); + } + else + { + column = (int) clientAnchor.getX1() - 1; + row = (int) clientAnchor.getY1() + 1; + } + + initialized = true; + } + + + /** + * Sets the object id. Invoked by the drawing group when the object is + * added to id + * + * @param objid the object id + * @param bip the blip id + * @param sid the shape id + */ + public final void setObjectId(int objid, int bip, int sid) + { + objectId = objid; + blipId = bip; + shapeId = sid; + + if (origin == Origin.READ) + { + origin = Origin.READ_WRITE; + } + } + + /** + * Accessor for the object id + * + * @return the object id + */ + public final int getObjectId() + { + if (!initialized) + { + initialize(); + } + + return objectId; + } + + /** + * Accessor for the shape id + * + * @return the object id + */ + public final int getShapeId() + { + if (!initialized) + { + initialize(); + } + + return shapeId; + } + + /** + * Accessor for the blip id + * + * @return the blip id + */ + public final int getBlipId() + { + if (!initialized) + { + initialize(); + } + + return blipId; + } + + /** + * Gets the drawing record which was read in + * + * @return the drawing record + */ + public MsoDrawingRecord getMsoDrawingRecord() + { + return msoDrawingRecord; + } + + /** + * Creates the main Sp container for the drawing + * + * @return the SP container + */ + public EscherContainer getSpContainer() + { + if (!initialized) + { + initialize(); + } + + if (origin == Origin.READ) + { + return getReadSpContainer(); + } + + Assert.verify(false); + + /* + if (spContainer == null) + { + spContainer = new SpContainer(); + Sp sp = new Sp(type, shapeId, 2560); + spContainer.add(sp); + Opt opt = new Opt(); + opt.addProperty(344, false, false, 0); // ? + opt.addProperty(385, false, false, 134217808); // fill colour + opt.addProperty(387, false, false, 134217808); // background colour + opt.addProperty(959, false, false, 131074); // hide + spContainer.add(opt); + + ClientAnchor clientAnchor = new ClientAnchor(column + 1.3, + Math.max(0, row - 0.6), + column+3, row + 4); + + spContainer.add(clientAnchor); + + ClientData clientData = new ClientData(); + spContainer.add(clientData); + + ClientTextBox clientTextBox = new ClientTextBox(); + spContainer.add(clientTextBox); + } + */ + + return spContainer; + } + + /** + * Sets the drawing group for this drawing. Called by the drawing group + * when this drawing is added to it + * + * @param dg the drawing group + */ + public void setDrawingGroup(DrawingGroup dg) + { + drawingGroup = dg; + } + + /** + * Accessor for the drawing group + * + * @return the drawing group + */ + public DrawingGroup getDrawingGroup() + { + return drawingGroup; + } + + /** + * Gets the origin of this drawing + * + * @return where this drawing came from + */ + public Origin getOrigin() + { + return origin; + } + + /** + * Accessor for the reference count on this drawing + * + * @return the reference count + */ + public int getReferenceCount() + { + return referenceCount; + } + + /** + * Sets the new reference count on the drawing + * + * @param r the new reference count + */ + public void setReferenceCount(int r) + { + referenceCount = r; + } + + /** + * Accessor for the column of this drawing + * + * @return the column + */ + public double getX() + { + if (!initialized) + { + initialize(); + } + return column; + } + + /** + * Sets the column position of this drawing. Used when inserting/removing + * columns from the spreadsheet + * + * @param x the column + */ + public void setX(double x) + { + if (origin == Origin.READ) + { + if (!initialized) + { + initialize(); + } + origin = Origin.READ_WRITE; + } + + column = (int) x; + } + + /** + * Accessor for the row of this drawing + * + * @return the row + */ + public double getY() + { + if (!initialized) + { + initialize(); + } + + return row; + } + + /** + * Accessor for the row of the drawing + * + * @param y the row + */ + public void setY(double y) + { + if (origin == Origin.READ) + { + if (!initialized) + { + initialize(); + } + origin = Origin.READ_WRITE; + } + + row = (int) y; + } + + + /** + * Accessor for the width of this drawing + * + * @return the number of columns spanned by this image + */ + public double getWidth() + { + if (!initialized) + { + initialize(); + } + + return width; + } + + /** + * Accessor for the width + * + * @param w the number of columns to span + */ + public void setWidth(double w) + { + if (origin == Origin.READ) + { + if (!initialized) + { + initialize(); + } + origin = Origin.READ_WRITE; + } + + width = w; + } + + /** + * Accessor for the height of this drawing + * + * @return the number of rows spanned by this image + */ + public double getHeight() + { + if (!initialized) + { + initialize(); + } + + return height; + } + + /** + * Accessor for the height of this drawing + * + * @param h the number of rows spanned by this image + */ + public void setHeight(double h) + { + if (origin == Origin.READ) + { + if (!initialized) + { + initialize(); + } + origin = Origin.READ_WRITE; + } + + height = h; + } + + + /** + * Gets the SpContainer that was read in + * + * @return the read sp container + */ + private EscherContainer getReadSpContainer() + { + if (!initialized) + { + initialize(); + } + + return readSpContainer; + } + + /** + * Accessor for the image data + * + * @return the image data + */ + public byte[] getImageData() + { + Assert.verify(origin == Origin.READ || origin == Origin.READ_WRITE); + + if (!initialized) + { + initialize(); + } + + return drawingGroup.getImageData(blipId); + } + + /** + * Accessor for the type + * + * @return the type + */ + public ShapeType getType() + { + return type; + } + + /** + * Sets the text object + * + * @param t the text object record + */ + public void setTextObject(TextObjectRecord t) + { + txo = t; + } + + /** + * Sets the text data + * + * @param t continuation record + */ + public void setText(ContinueRecord t) + { + text = t; + } + + /** + * Sets the formatting + * + * @param t continue record + */ + public void setFormatting(ContinueRecord t) + { + formatting = t; + } + + /** + * Accessor for the image data + * + * @return the image data + */ + public byte[] getImageBytes() + { + Assert.verify(false); + return null; + } + + /** + * Accessor for the image file path. Normally this is the absolute path + * of a file on the directory system, but if this drawing was constructed + * using an byte[] then the blip id is returned + * + * @return the image file path, or the blip id + */ + public String getImageFilePath() + { + Assert.verify(false); + return null; + } + + /** + * The drawing record + * + * @param d the drawing record + */ + public void addMso(MsoDrawingRecord d) + { + mso = d; + drawingData.addRawData(mso.getData()); + } + + /** + * Writes out any additional records + * + * @param outputFile the output file + * @exception IOException + */ + public void writeAdditionalRecords(File outputFile) throws IOException + { + if (origin == Origin.READ) + { + outputFile.write(objRecord); + + if (mso != null) + { + outputFile.write(mso); + } + outputFile.write(txo); + outputFile.write(text); + if (formatting != null) + { + outputFile.write(formatting); + } + return; + } + + Assert.verify(false); + + // Create the obj record + ObjRecord objrec = new ObjRecord(objectId, + ObjRecord.EXCELNOTE); + + outputFile.write(objrec); + + // Create the mso data record. Write the text box record again, + // although it is already included in the SpContainer + ClientTextBox textBox = new ClientTextBox(); + MsoDrawingRecord msod = new MsoDrawingRecord(textBox.getData()); + outputFile.write(msod); + + TextObjectRecord tor = new TextObjectRecord(getText()); + outputFile.write(tor); + + // Data for the first continue record + byte[] textData = new byte[commentText.length() * 2 + 1]; + textData[0] = 0x1; // unicode indicator + StringHelper.getUnicodeBytes(commentText, textData, 1); + //StringHelper.getBytes(commentText, textData, 1); + ContinueRecord textContinue = new ContinueRecord(textData); + outputFile.write(textContinue); + + // Data for the formatting runs + + byte[] frData = new byte[16]; + + // First txo run (the user) + IntegerHelper.getTwoBytes(0, frData, 0); // index to the first character + IntegerHelper.getTwoBytes(0, frData, 2); // index to the font(default) + // Mandatory last txo run + IntegerHelper.getTwoBytes(commentText.length(), frData, 8); + IntegerHelper.getTwoBytes(0, frData, 10); // index to the font(default) + + ContinueRecord frContinue = new ContinueRecord(frData); + outputFile.write(frContinue); + } + + /** + * Writes any records that need to be written after all the drawing group + * objects have been written + * Writes out all the note records + * + * @param outputFile the output file + */ + public void writeTailRecords(File outputFile) + { + } + + /** + * Accessor for the row. As buttons are not associated with a cell, + * does nothing here + * + * @return the row number + */ + public int getRow() + { + return 0; + } + + /** + * Accessor for the column. As buttons are not associated with a cell, + * does nothing here + * + * @return the column number + */ + public int getColumn() + { + return 0; + } + + /** + * Accessor for the text on the button + * + * @return the button text + */ + public String getText() + { + if (commentText == null) + { + Assert.verify(text != null); + + byte[] td = text.getData(); + if (td[0] == 0) + { + commentText = StringHelper.getString + (td, td.length - 1, 1, workbookSettings); + } + else + { + commentText = StringHelper.getUnicodeString + (td, (td.length - 1) / 2, 1); + } + } + + return commentText; + } + + /** + * Hashing algorithm + * + * @return the hash code + */ + public int hashCode() + { + return commentText.hashCode(); + } + + /** + * Called when the comment text is changed during the sheet copy process + * + * @param t the new text + */ + public void setButtonText(String t) + { + commentText = t; + + if (origin == Origin.READ) + { + origin = Origin.READ_WRITE; + } + } + + /** + * Accessor for the first drawing on the sheet. This is used when + * copying unmodified sheets to indicate that this drawing contains + * the first time Escher gubbins + * + * @return TRUE if this MSORecord is the first drawing on the sheet + */ + public boolean isFirst() + { + return mso.isFirst(); + } + + /** + * Queries whether this object is a form object. Form objects have their + * drawings records spread over TXO and CONTINUE records and + * require special handling + * + * @return TRUE if this is a form object, FALSE otherwise + */ + public boolean isFormObject() + { + return true; + } +} + + + Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/Chart.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/Chart.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/Chart.java 17 Aug 2012 14:51:19 -0000 1.1 @@ -0,0 +1,270 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +import common.Assert; +import common.Logger; + +import jxl.WorkbookSettings; +import jxl.biff.ByteData; +import jxl.biff.IndexMapping; +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.read.biff.File; + +/** + * Contains the various biff records used to insert a chart into a + * worksheet + */ +public class Chart implements ByteData, EscherStream +{ + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(Chart.class); + + /** + * The MsoDrawingRecord associated with the chart + */ + private MsoDrawingRecord msoDrawingRecord; + + /** + * The ObjRecord associated with the chart + */ + private ObjRecord objRecord; + + /** + * The start pos of the chart bof stream in the data file + */ + private int startpos; + + /** + * The start pos of the chart bof stream in the data file + */ + private int endpos; + + /** + * A handle to the Excel file + */ + private File file; + + /** + * The drawing data + */ + private DrawingData drawingData; + + /** + * The drawing number + */ + private int drawingNumber; + + /** + * The chart byte data + */ + private byte[] data; + + /** + * Flag which indicates that the byte data has been initialized + */ + private boolean initialized; + + /** + * The workbook settings + */ + private WorkbookSettings workbookSettings; + + /** + * Constructor + * + * @param mso a MsoDrawingRecord value + * @param obj an ObjRecord value + * @param dd the drawing data + * @param sp an int value + * @param ep an int value + * @param f a File value + * @param ws the workbook settings + */ + public Chart(MsoDrawingRecord mso, + ObjRecord obj, + DrawingData dd, + int sp, int ep, File f, WorkbookSettings ws) + { + msoDrawingRecord = mso; + objRecord = obj; + startpos = sp; + endpos = ep; + file = f; + workbookSettings = ws; + + // msoDrawingRecord is null if the entire sheet consists of just the + // chart. In this case, as there is only one drawing on the page, + // it isn't necessary to add to the drawing data record anyway + if (msoDrawingRecord != null) + { + drawingData = dd; + drawingData.addData(msoDrawingRecord.getRecord().getData()); + drawingNumber = drawingData.getNumDrawings() - 1; + } + + initialized = false; + + // Note: mso and obj values can be null if we are creating a chart + // which takes up an entire worksheet. Check that both are null or both + // not null though + Assert.verify((mso != null && obj != null) || + (mso == null && obj == null)); + } + + /** + * Gets the entire binary record for the chart as a chunk of binary data + * + * @return the bytes + */ + public byte[] getBytes() + { + if (!initialized) + { + initialize(); + } + + return data; + } + + /** + * Implementation of the EscherStream method + * + * @return the data + */ + public byte[] getData() + { + return msoDrawingRecord.getRecord().getData(); + } + + /** + * Initializes the charts byte data + */ + private void initialize() + { + data = file.read(startpos, endpos - startpos); + initialized = true; + } + + /** + * Rationalizes the sheet's xf index mapping + * @param xfMapping the index mapping for XFRecords + * @param fontMapping the index mapping for fonts + * @param formatMapping the index mapping for formats + */ + public void rationalize(IndexMapping xfMapping, + IndexMapping fontMapping, + IndexMapping formatMapping) + { + if (!initialized) + { + initialize(); + } + + // Read through the array, looking for the data types + // This is a total hack bodge for now - it will eventually need to be + // integrated properly + int pos = 0; + int code = 0; + int length = 0; + Type type = null; + while (pos < data.length) + { + code = IntegerHelper.getInt(data[pos], data[pos + 1]); + length = IntegerHelper.getInt(data[pos + 2], data[pos + 3]); + + type = Type.getType(code); + + if (type == Type.FONTX) + { + int fontind = IntegerHelper.getInt(data[pos + 4], data[pos + 5]); + IntegerHelper.getTwoBytes(fontMapping.getNewIndex(fontind), + data, pos + 4); + } + else if (type == Type.FBI) + { + int fontind = IntegerHelper.getInt(data[pos + 12], data[pos + 13]); + IntegerHelper.getTwoBytes(fontMapping.getNewIndex(fontind), + data, pos + 12); + } + else if (type == Type.IFMT) + { + int formind = IntegerHelper.getInt(data[pos + 4], data[pos + 5]); + IntegerHelper.getTwoBytes(formatMapping.getNewIndex(formind), + data, pos + 4); + } + else if (type == Type.ALRUNS) + { + int numRuns = IntegerHelper.getInt(data[pos + 4], data[pos + 5]); + int fontPos = pos + 6; + for (int i = 0 ; i < numRuns; i++) + { + int fontind = IntegerHelper.getInt(data[fontPos + 2], + data[fontPos + 3]); + IntegerHelper.getTwoBytes(fontMapping.getNewIndex(fontind), + data, fontPos + 2); + fontPos += 4; + } + } + + pos += length + 4; + } + } + + /** + * Gets the SpContainer containing the charts drawing information + * + * @return the spContainer + */ + EscherContainer getSpContainer() + { + EscherContainer spContainer = drawingData.getSpContainer(drawingNumber); + + return spContainer; + } + + /** + * Accessor for the mso drawing record + * + * @return the drawing record + */ + MsoDrawingRecord getMsoDrawingRecord() + { + return msoDrawingRecord; + } + + /** + * Accessor for the obj record + * + * @return the obj record + */ + ObjRecord getObjRecord() + { + return objRecord; + } +} + + + + + Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/Chunk.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/Chunk.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/Chunk.java 17 Aug 2012 14:51:19 -0000 1.1 @@ -0,0 +1,43 @@ +/********************************************************************* +* +* Copyright (C) 2006 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +class Chunk +{ + private int pos; + private int length; + private ChunkType type; + private byte[] data; + + public Chunk(int p, int l, ChunkType ct, byte[] d) + { + pos = p; + length = l; + type = ct; + data = new byte[length]; + System.arraycopy(d, pos, data, 0, length); + + } + + public byte[] getData() + { + return data; + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/ChunkType.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/ChunkType.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/ChunkType.java 17 Aug 2012 14:51:19 -0000 1.1 @@ -0,0 +1,74 @@ +/********************************************************************* +* +* Copyright (C) 2006 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +import java.util.Arrays; + +/** + * Enumeration for the various chunk types + */ +class ChunkType +{ + private byte[] id; + private String name; + + private static ChunkType[] chunkTypes = new ChunkType[0]; + + private ChunkType(int d1, int d2, int d3, int d4, String n) + { + id = new byte[] {(byte) d1, (byte) d2, (byte) d3, (byte) d4}; + name = n; + + ChunkType[] ct = new ChunkType[chunkTypes.length + 1]; + System.arraycopy(chunkTypes, 0, ct, 0, chunkTypes.length); + ct[chunkTypes.length] = this; + chunkTypes = ct; + } + + public String getName() + { + return name; + } + + public static ChunkType getChunkType(byte d1, byte d2, byte d3, byte d4) + { + byte[] cmp = new byte[] {d1, d2, d3, d4}; + + boolean found = false; + ChunkType chunk = ChunkType.UNKNOWN; + + for (int i = 0; i < chunkTypes.length && !found ; i++) + { + if (Arrays.equals(chunkTypes[i].id, cmp)) + { + chunk = chunkTypes[i]; + found = true; + } + } + + return chunk; + } + + + public static ChunkType IHDR = new ChunkType(0x49, 0x48, 0x44, 0x52,"IHDR"); + public static ChunkType IEND = new ChunkType(0x49, 0x45, 0x4e, 0x44,"IEND"); + public static ChunkType PHYS = new ChunkType(0x70, 0x48, 0x59, 0x73,"pHYs"); + public static ChunkType UNKNOWN = new ChunkType(0xff, 0xff, 0xff, 0xff, "UNKNOWN"); +} Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/ClientAnchor.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/ClientAnchor.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/ClientAnchor.java 17 Aug 2012 14:51:19 -0000 1.1 @@ -0,0 +1,211 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +import common.Logger; + +import jxl.biff.IntegerHelper; + +/** + * The client anchor record + */ +class ClientAnchor extends EscherAtom +{ + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(ClientAnchor.class); + + /** + * The binary data + */ + private byte[] data; + + /** + * The properties + */ + private int properties; + + /** + * The x1 position + */ + private double x1; + + /** + * The y1 position + */ + private double y1; + + /** + * The x2 position + */ + private double x2; + + /** + * The y2 position + */ + private double y2; + + /** + * Constructor + * + * @param erd the escher record data + */ + public ClientAnchor(EscherRecordData erd) + { + super(erd); + byte[] bytes = getBytes(); + + // The properties + properties = IntegerHelper.getInt(bytes[0], bytes[1]); + + // The x1 cell + int x1Cell = IntegerHelper.getInt(bytes[2], bytes[3]); + int x1Fraction = IntegerHelper.getInt(bytes[4], bytes[5]); + + x1 = x1Cell + (double) x1Fraction / (double) 1024; + + // The y1 cell + int y1Cell = IntegerHelper.getInt(bytes[6], bytes[7]); + int y1Fraction = IntegerHelper.getInt(bytes[8], bytes[9]); + + y1 = y1Cell + (double) y1Fraction / (double) 256; + + // The x2 cell + int x2Cell = IntegerHelper.getInt(bytes[10], bytes[11]); + int x2Fraction = IntegerHelper.getInt(bytes[12], bytes[13]); + + x2 = x2Cell + (double) x2Fraction / (double) 1024; + + // The y1 cell + int y2Cell = IntegerHelper.getInt(bytes[14], bytes[15]); + int y2Fraction = IntegerHelper.getInt(bytes[16], bytes[17]); + + y2 = y2Cell + (double) y2Fraction / (double) 256; + } + + /** + * Constructor + * + * @param x1 the x1 position + * @param y1 the y1 position + * @param x2 the x2 position + * @param y2 the y2 position + * @param props the anchor properties + */ + public ClientAnchor(double x1, double y1, double x2, double y2, int props) + { + super(EscherRecordType.CLIENT_ANCHOR); + this.x1 = x1; + this.y1 = y1; + this.x2 = x2; + this.y2 = y2; + properties = props; + } + + /** + * Gets the client anchor data + * + * @return the data + */ + byte[] getData() + { + data = new byte[18]; + IntegerHelper.getTwoBytes(properties, data, 0); + + // The x1 cell + IntegerHelper.getTwoBytes((int) x1, data, 2); + + // The x1 fraction into the cell 0-1024 + int x1fraction = (int) ((x1 - (int) x1) * 1024); + IntegerHelper.getTwoBytes(x1fraction, data, 4); + + // The y1 cell + IntegerHelper.getTwoBytes((int) y1, data, 6); + + // The y1 fraction into the cell 0-256 + int y1fraction = (int) ((y1 - (int) y1) * 256); + IntegerHelper.getTwoBytes(y1fraction, data, 8); + + // The x2 cell + IntegerHelper.getTwoBytes((int) x2, data, 10); + + // The x2 fraction into the cell 0-1024 + int x2fraction = (int) ((x2 - (int) x2) * 1024); + IntegerHelper.getTwoBytes(x2fraction, data, 12); + + // The y2 cell + IntegerHelper.getTwoBytes((int) y2, data, 14); + + // The y2 fraction into the cell 0-256 + int y2fraction = (int) ((y2 - (int) y2) * 256); + IntegerHelper.getTwoBytes(y2fraction, data, 16); + + return setHeaderData(data); + } + + /** + * Accessor for the x1 position + * + * @return the x1 position + */ + double getX1() + { + return x1; + } + + /** + * Accessor for the y1 position + * + * @return the y1 position + */ + double getY1() + { + return y1; + } + + /** + * Accessor for the x2 position + * + * @return the x2 position + */ + double getX2() + { + return x2; + } + + /** + * Accessor for the y2 position + * + * @return the y2 position + */ + double getY2() + { + return y2; + } + + /** + * Accessor for the anchor properties + */ + int getProperties() + { + return properties; + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/ClientData.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/ClientData.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/ClientData.java 17 Aug 2012 14:51:20 -0000 1.1 @@ -0,0 +1,67 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +import common.Logger; + +/** + * The client data + */ +class ClientData extends EscherAtom +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(ClientData.class); + + /** + * The raw data + */ + private byte[] data; + + /** + * Constructor + * + * @param erd the record data + */ + public ClientData(EscherRecordData erd) + { + super(erd); + } + + /** + * Constructor + */ + public ClientData() + { + super(EscherRecordType.CLIENT_DATA); + } + + /** + * Accessor for the raw data + * + * @return the binary data + */ + byte[] getData() + { + data = new byte[0]; + return setHeaderData(data); + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/ClientTextBox.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/ClientTextBox.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/ClientTextBox.java 17 Aug 2012 14:51:20 -0000 1.1 @@ -0,0 +1,67 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +import common.Logger; + +/** + * ??? + */ +class ClientTextBox extends EscherAtom +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(ClientTextBox.class); + + /** + * The raw data + */ + private byte[] data; + + /** + * Constructor + * + * @param erd + */ + public ClientTextBox(EscherRecordData erd) + { + super(erd); + } + + /** + * Constructor + */ + public ClientTextBox() + { + super(EscherRecordType.CLIENT_TEXT_BOX); + } + + /** + * Accessor for the raw data + * + * @return + */ + byte[] getData() + { + data = new byte[0]; + return setHeaderData(data); + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/ComboBox.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/ComboBox.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/ComboBox.java 17 Aug 2012 14:51:19 -0000 1.1 @@ -0,0 +1,706 @@ +/********************************************************************* +* +* Copyright (C) 2006 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +import java.io.IOException; + +import common.Assert; +import common.Logger; + +import jxl.WorkbookSettings; +import jxl.write.biff.File; + +/** + * Contains the various biff records used to copy a ComboBox (from the + * Form toolbox) between workbook + */ +public class ComboBox implements DrawingGroupObject +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(ComboBox.class); + + /** + * The spContainer that was read in + */ + private EscherContainer readSpContainer; + + /** + * The spContainer that was generated + */ + private EscherContainer spContainer; + + /** + * The MsoDrawingRecord associated with the drawing + */ + private MsoDrawingRecord msoDrawingRecord; + + /** + * The ObjRecord associated with the drawing + */ + private ObjRecord objRecord; + + /** + * Initialized flag + */ + private boolean initialized = false; + + /** + * The object id, assigned by the drawing group + */ + private int objectId; + + /** + * The blip id + */ + private int blipId; + + /** + * The shape id + */ + private int shapeId; + + /** + * The column + */ + private int column; + + /** + * The row position of the image + */ + private int row; + + /** + * The width of the image in cells + */ + private double width; + + /** + * The height of the image in cells + */ + private double height; + + /** + * The number of places this drawing is referenced + */ + private int referenceCount; + + /** + * The top level escher container + */ + private EscherContainer escherData; + + /** + * Where this image came from (read, written or a copy) + */ + private Origin origin; + + /** + * The drawing group for all the images + */ + private DrawingGroup drawingGroup; + + /** + * The drawing data + */ + private DrawingData drawingData; + + /** + * The type of this drawing object + */ + private ShapeType type; + + /** + * The drawing position on the sheet + */ + private int drawingNumber; + + /** + * The workbook settings + */ + private WorkbookSettings workbookSettings; + + /** + * Constructor used when reading images + * + * @param mso the drawing record + * @param obj the object record + * @param dd the drawing data for all drawings on this sheet + * @param dg the drawing group + * @param ws the workbook settings + */ + public ComboBox(MsoDrawingRecord mso, ObjRecord obj, DrawingData dd, + DrawingGroup dg, WorkbookSettings ws) + { + drawingGroup = dg; + msoDrawingRecord = mso; + drawingData = dd; + objRecord = obj; + initialized = false; + workbookSettings = ws; + origin = Origin.READ; + drawingData.addData(msoDrawingRecord.getData()); + drawingNumber = drawingData.getNumDrawings() - 1; + drawingGroup.addDrawing(this); + + Assert.verify(mso != null && obj != null); + + initialize(); + } + + /** + * Copy constructor used to copy drawings from read to write + * + * @param dgo the drawing group object + * @param dg the drawing group + * @param ws the workbook settings + */ + public ComboBox(DrawingGroupObject dgo, + DrawingGroup dg, + WorkbookSettings ws) + { + ComboBox d = (ComboBox) dgo; + Assert.verify(d.origin == Origin.READ); + msoDrawingRecord = d.msoDrawingRecord; + objRecord = d.objRecord; + initialized = false; + origin = Origin.READ; + drawingData = d.drawingData; + drawingGroup = dg; + drawingNumber = d.drawingNumber; + drawingGroup.addDrawing(this); + workbookSettings = ws; + } + + /** + * Constructor invoked when writing images + */ + public ComboBox() + { + initialized = true; + origin = Origin.WRITE; + referenceCount = 1; + type = ShapeType.HOST_CONTROL; + } + + /** + * Initializes the member variables from the Escher stream data + */ + private void initialize() + { + readSpContainer = drawingData.getSpContainer(drawingNumber); + Assert.verify(readSpContainer != null); + + EscherRecord[] children = readSpContainer.getChildren(); + + Sp sp = (Sp) readSpContainer.getChildren()[0]; + objectId = objRecord.getObjectId(); + shapeId = sp.getShapeId(); + type = ShapeType.getType(sp.getShapeType()); + + if (type == ShapeType.UNKNOWN) + { + logger.warn("Unknown shape type"); + } + + ClientAnchor clientAnchor = null; + for (int i = 0; i < children.length && clientAnchor == null; i++) + { + if (children[i].getType() == EscherRecordType.CLIENT_ANCHOR) + { + clientAnchor = (ClientAnchor) children[i]; + } + } + + if (clientAnchor == null) + { + logger.warn("Client anchor not found"); + } + else + { + column = (int) clientAnchor.getX1(); + row = (int) clientAnchor.getY1(); + } + + initialized = true; + } + + + /** + * Sets the object id. Invoked by the drawing group when the object is + * added to id + * + * @param objid the object id + * @param bip the blip id + * @param sid the shape id + */ + public final void setObjectId(int objid, int bip, int sid) + { + objectId = objid; + blipId = bip; + shapeId = sid; + + if (origin == Origin.READ) + { + origin = Origin.READ_WRITE; + } + } + + /** + * Accessor for the object id + * + * @return the object id + */ + public final int getObjectId() + { + if (!initialized) + { + initialize(); + } + + return objectId; + } + + /** + * Accessor for the shape id + * + * @return the object id + */ + public final int getShapeId() + { + if (!initialized) + { + initialize(); + } + + return shapeId; + } + + /** + * Accessor for the blip id + * + * @return the blip id + */ + public final int getBlipId() + { + if (!initialized) + { + initialize(); + } + + return blipId; + } + + /** + * Gets the drawing record which was read in + * + * @return the drawing record + */ + public MsoDrawingRecord getMsoDrawingRecord() + { + return msoDrawingRecord; + } + + /** + * Creates the main Sp container for the drawing + * + * @return the SP container + */ + public EscherContainer getSpContainer() + { + if (!initialized) + { + initialize(); + } + + if (origin == Origin.READ) + { + return getReadSpContainer(); + } + + SpContainer spc = new SpContainer(); + Sp sp = new Sp(type, shapeId, 2560); + spc.add(sp); + Opt opt = new Opt(); + opt.addProperty(127, false, false, 17039620); + opt.addProperty(191, false, false, 524296); + opt.addProperty(511, false, false, 524288); + opt.addProperty(959, false, false, 131072); + // opt.addProperty(260, true, false, blipId); + // opt.addProperty(261, false, false, 36); + spc.add(opt); + + ClientAnchor clientAnchor = new ClientAnchor(column, + row, + column + 1, + row + 1, + 0x1); + spc.add(clientAnchor); + ClientData clientData = new ClientData(); + spc.add(clientData); + + return spc; + } + + /** + * Sets the drawing group for this drawing. Called by the drawing group + * when this drawing is added to it + * + * @param dg the drawing group + */ + public void setDrawingGroup(DrawingGroup dg) + { + drawingGroup = dg; + } + + /** + * Accessor for the drawing group + * + * @return the drawing group + */ + public DrawingGroup getDrawingGroup() + { + return drawingGroup; + } + + /** + * Gets the origin of this drawing + * + * @return where this drawing came from + */ + public Origin getOrigin() + { + return origin; + } + + /** + * Accessor for the reference count on this drawing + * + * @return the reference count + */ + public int getReferenceCount() + { + return referenceCount; + } + + /** + * Sets the new reference count on the drawing + * + * @param r the new reference count + */ + public void setReferenceCount(int r) + { + referenceCount = r; + } + + /** + * Accessor for the column of this drawing + * + * @return the column + */ + public double getX() + { + if (!initialized) + { + initialize(); + } + return column; + } + + /** + * Sets the column position of this drawing. Used when inserting/removing + * columns from the spreadsheet + * + * @param x the column + */ + public void setX(double x) + { + if (origin == Origin.READ) + { + if (!initialized) + { + initialize(); + } + origin = Origin.READ_WRITE; + } + + column = (int) x; + } + + /** + * Accessor for the row of this drawing + * + * @return the row + */ + public double getY() + { + if (!initialized) + { + initialize(); + } + + return row; + } + + /** + * Accessor for the row of the drawing + * + * @param y the row + */ + public void setY(double y) + { + if (origin == Origin.READ) + { + if (!initialized) + { + initialize(); + } + origin = Origin.READ_WRITE; + } + + row = (int) y; + } + + + /** + * Accessor for the width of this drawing + * + * @return the number of columns spanned by this image + */ + public double getWidth() + { + if (!initialized) + { + initialize(); + } + + return width; + } + + /** + * Accessor for the width + * + * @param w the number of columns to span + */ + public void setWidth(double w) + { + if (origin == Origin.READ) + { + if (!initialized) + { + initialize(); + } + origin = Origin.READ_WRITE; + } + + width = w; + } + + /** + * Accessor for the height of this drawing + * + * @return the number of rows spanned by this image + */ + public double getHeight() + { + if (!initialized) + { + initialize(); + } + + return height; + } + + /** + * Accessor for the height of this drawing + * + * @param h the number of rows spanned by this image + */ + public void setHeight(double h) + { + if (origin == Origin.READ) + { + if (!initialized) + { + initialize(); + } + origin = Origin.READ_WRITE; + } + + height = h; + } + + + /** + * Gets the SpContainer that was read in + * + * @return the read sp container + */ + private EscherContainer getReadSpContainer() + { + if (!initialized) + { + initialize(); + } + + return readSpContainer; + } + + /** + * Accessor for the image data + * + * @return the image data + */ + public byte[] getImageData() + { + Assert.verify(origin == Origin.READ || origin == Origin.READ_WRITE); + + if (!initialized) + { + initialize(); + } + + return drawingGroup.getImageData(blipId); + } + + /** + * Accessor for the type + * + * @return the type + */ + public ShapeType getType() + { + return type; + } + + /** + * Accessor for the image data + * + * @return the image data + */ + public byte[] getImageBytes() + { + Assert.verify(false); + return null; + } + + /** + * Accessor for the image file path. Normally this is the absolute path + * of a file on the directory system, but if this drawing was constructed + * using an byte[] then the blip id is returned + * + * @return the image file path, or the blip id + */ + public String getImageFilePath() + { + Assert.verify(false); + return null; + } + + /** + * Writes out the additional records for a combo box + * + * @param outputFile the output file + * @exception IOException + */ + public void writeAdditionalRecords(File outputFile) throws IOException + { + if (origin == Origin.READ) + { + outputFile.write(objRecord); + return; + } + + // Create the obj record + ObjRecord objrec = new ObjRecord(objectId, + ObjRecord.COMBOBOX); + + outputFile.write(objrec); + } + + /** + * Writes any records that need to be written after all the drawing group + * objects have been written + * Writes out all the note records + * + * @param outputFile the output file + */ + public void writeTailRecords(File outputFile) + { + } + + /** + * Accessor for the row + * + * @return the row + */ + public int getRow() + { + return 0; + } + + /** + * Accessor for the column + * + * @return the column + */ + public int getColumn() + { + return 0; + } + + /** + * Hashing algorithm + * + * @return the hash code + */ + public int hashCode() + { + return getClass().getName().hashCode(); + } + + /** + * Accessor for the first drawing on the sheet. This is used when + * copying unmodified sheets to indicate that this drawing contains + * the first time Escher gubbins + * + * @return TRUE if this MSORecord is the first drawing on the sheet + */ + public boolean isFirst() + { + return msoDrawingRecord.isFirst(); + } + + /** + * Queries whether this object is a form object. Form objects have their + * drawings records spread over TXO and CONTINUE records and + * require special handling + * + * @return TRUE if this is a form object, FALSE otherwise + */ + public boolean isFormObject() + { + return false; + } +} + + + Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/Comment.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/Comment.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/Comment.java 17 Aug 2012 14:51:19 -0000 1.1 @@ -0,0 +1,910 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +import java.io.IOException; + +import common.Assert; +import common.Logger; + +import jxl.WorkbookSettings; +import jxl.biff.ContinueRecord; +import jxl.biff.IntegerHelper; +import jxl.biff.StringHelper; +import jxl.write.biff.File; + +/** + * Contains the various biff records used to insert a cell note into a + * worksheet + */ +public class Comment implements DrawingGroupObject +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(Comment.class); + + /** + * The spContainer that was read in + */ + private EscherContainer readSpContainer; + + /** + * The spContainer that was generated + */ + private EscherContainer spContainer; + + /** + * The MsoDrawingRecord associated with the drawing + */ + private MsoDrawingRecord msoDrawingRecord; + + /** + * The ObjRecord associated with the drawing + */ + private ObjRecord objRecord; + + /** + * Initialized flag + */ + private boolean initialized = false; + + /** + * The object id, assigned by the drawing group + */ + private int objectId; + + /** + * The blip id + */ + private int blipId; + + /** + * The shape id + */ + private int shapeId; + + /** + * The column + */ + private int column; + + /** + * The row position of the image + */ + private int row; + + /** + * The width of the image in cells + */ + private double width; + + /** + * The height of the image in cells + */ + private double height; + + /** + * The number of places this drawing is referenced + */ + private int referenceCount; + + /** + * The top level escher container + */ + private EscherContainer escherData; + + /** + * Where this image came from (read, written or a copy) + */ + private Origin origin; + + /** + * The drawing group for all the images + */ + private DrawingGroup drawingGroup; + + /** + * The drawing data + */ + private DrawingData drawingData; + + /** + * The type of this drawing object + */ + private ShapeType type; + + /** + * The drawing position on the sheet + */ + private int drawingNumber; + + /** + * An mso drawing record, which sometimes appears + */ + private MsoDrawingRecord mso; + + /** + * The text object record + */ + private TextObjectRecord txo; + + /** + * The note record + */ + private NoteRecord note; + + /** + * Text data from the first continue record + */ + private ContinueRecord text; + + /** + * Formatting data from the second continue record + */ + private ContinueRecord formatting; + + /** + * The comment text + */ + private String commentText; + + /** + * The workbook settings + */ + private WorkbookSettings workbookSettings; + + /** + * Constructor used when reading images + * + * @param msorec the drawing record + * @param obj the object record + * @param dd the drawing data for all drawings on this sheet + * @param dg the drawing group + * @param ws the workbook settings + */ + public Comment(MsoDrawingRecord msorec, ObjRecord obj, DrawingData dd, + DrawingGroup dg, WorkbookSettings ws) + { + drawingGroup = dg; + msoDrawingRecord = msorec; + drawingData = dd; + objRecord = obj; + initialized = false; + workbookSettings = ws; + origin = Origin.READ; + drawingData.addData(msoDrawingRecord.getData()); + drawingNumber = drawingData.getNumDrawings() - 1; + drawingGroup.addDrawing(this); + + Assert.verify(msoDrawingRecord != null && objRecord != null); + + if (!initialized) + { + initialize(); + } + } + + /** + * Copy constructor used to copy drawings from read to write + * + * @param dgo the drawing group object + * @param dg the drawing group + * @param ws the workbook settings + */ + /*protected*/ public Comment(DrawingGroupObject dgo, + DrawingGroup dg, + WorkbookSettings ws) + { + Comment d = (Comment) dgo; + Assert.verify(d.origin == Origin.READ); + msoDrawingRecord = d.msoDrawingRecord; + objRecord = d.objRecord; + initialized = false; + origin = Origin.READ; + drawingData = d.drawingData; + drawingGroup = dg; + drawingNumber = d.drawingNumber; + drawingGroup.addDrawing(this); + mso = d.mso; + txo = d.txo; + text = d.text; + formatting = d.formatting; + note = d.note; + width = d.width; + height = d.height; + workbookSettings = ws; + } + + /** + * Constructor invoked when writing the images + * + * @param txt the comment text + * @param c the column + * @param r the row + */ + public Comment(String txt, int c, int r) + { + initialized = true; + origin = Origin.WRITE; + column = c; + row = r; + referenceCount = 1; + type = ShapeType.TEXT_BOX; + commentText = txt; + width = 3; + height = 4; + } + + /** + * Initializes the member variables from the Escher stream data + */ + private void initialize() + { + readSpContainer = drawingData.getSpContainer(drawingNumber); + Assert.verify(readSpContainer != null); + + EscherRecord[] children = readSpContainer.getChildren(); + + Sp sp = (Sp) readSpContainer.getChildren()[0]; + objectId = objRecord.getObjectId(); + shapeId = sp.getShapeId(); + type = ShapeType.getType(sp.getShapeType()); + + if (type == ShapeType.UNKNOWN) + { + logger.warn("Unknown shape type"); + } + + ClientAnchor clientAnchor = null; + for (int i = 0; i < children.length && clientAnchor == null; i++) + { + if (children[i].getType() == EscherRecordType.CLIENT_ANCHOR) + { + clientAnchor = (ClientAnchor) children[i]; + } + } + + if (clientAnchor == null) + { + logger.warn("client anchor not found"); + } + else + { + column = (int) clientAnchor.getX1() - 1; + row = (int) clientAnchor.getY1() + 1; + width = clientAnchor.getX2() - clientAnchor.getX1(); + height = clientAnchor.getY2() - clientAnchor.getY1(); + } + + initialized = true; + } + + + /** + * Sets the object id. Invoked by the drawing group when the object is + * added to id + * + * @param objid the object id + * @param bip the blip id + * @param sid the shape id + */ + public final void setObjectId(int objid, int bip, int sid) + { + objectId = objid; + blipId = bip; + shapeId = sid; + + if (origin == Origin.READ) + { + origin = Origin.READ_WRITE; + } + } + + /** + * Accessor for the object id + * + * @return the object id + */ + public final int getObjectId() + { + if (!initialized) + { + initialize(); + } + + return objectId; + } + + /** + * Accessor for the shape id + * + * @return the object id + */ + public final int getShapeId() + { + if (!initialized) + { + initialize(); + } + + return shapeId; + } + + /** + * Accessor for the blip id + * + * @return the blip id + */ + public final int getBlipId() + { + if (!initialized) + { + initialize(); + } + + return blipId; + } + + /** + * Gets the drawing record which was read in + * + * @return the drawing record + */ + public MsoDrawingRecord getMsoDrawingRecord() + { + return msoDrawingRecord; + } + + /** + * Creates the main Sp container for the drawing + * + * @return the SP container + */ + public EscherContainer getSpContainer() + { + if (!initialized) + { + initialize(); + } + + if (origin == Origin.READ) + { + return getReadSpContainer(); + } + + if (spContainer == null) + { + spContainer = new SpContainer(); + Sp sp = new Sp(type, shapeId, 2560); + spContainer.add(sp); + Opt opt = new Opt(); + opt.addProperty(344, false, false, 0); // ? + opt.addProperty(385, false, false, 134217808); // fill colour + opt.addProperty(387, false, false, 134217808); // background colour + opt.addProperty(959, false, false, 131074); // hide + spContainer.add(opt); + + ClientAnchor clientAnchor = new ClientAnchor(column + 1.3, + Math.max(0, row - 0.6), + column + 1.3 + width, + row + height, + 0x1); + + spContainer.add(clientAnchor); + + ClientData clientData = new ClientData(); + spContainer.add(clientData); + + ClientTextBox clientTextBox = new ClientTextBox(); + spContainer.add(clientTextBox); + } + + return spContainer; + } + + /** + * Sets the drawing group for this drawing. Called by the drawing group + * when this drawing is added to it + * + * @param dg the drawing group + */ + public void setDrawingGroup(DrawingGroup dg) + { + drawingGroup = dg; + } + + /** + * Accessor for the drawing group + * + * @return the drawing group + */ + public DrawingGroup getDrawingGroup() + { + return drawingGroup; + } + + /** + * Gets the origin of this drawing + * + * @return where this drawing came from + */ + public Origin getOrigin() + { + return origin; + } + + /** + * Accessor for the reference count on this drawing + * + * @return the reference count + */ + public int getReferenceCount() + { + return referenceCount; + } + + /** + * Sets the new reference count on the drawing + * + * @param r the new reference count + */ + public void setReferenceCount(int r) + { + referenceCount = r; + } + + /** + * Accessor for the column of this drawing + * + * @return the column + */ + public double getX() + { + if (!initialized) + { + initialize(); + } + return column; + } + + /** + * Sets the column position of this drawing. Used when inserting/removing + * columns from the spreadsheet + * + * @param x the column + */ + public void setX(double x) + { + if (origin == Origin.READ) + { + if (!initialized) + { + initialize(); + } + origin = Origin.READ_WRITE; + } + + column = (int) x; + } + + /** + * Accessor for the row of this drawing + * + * @return the row + */ + public double getY() + { + if (!initialized) + { + initialize(); + } + + return row; + } + + /** + * Accessor for the row of the drawing + * + * @param y the row + */ + public void setY(double y) + { + if (origin == Origin.READ) + { + if (!initialized) + { + initialize(); + } + origin = Origin.READ_WRITE; + } + + row = (int) y; + } + + + /** + * Accessor for the width of this drawing + * + * @return the number of columns spanned by this image + */ + public double getWidth() + { + if (!initialized) + { + initialize(); + } + + return width; + } + + /** + * Accessor for the width + * + * @param w the number of columns to span + */ + public void setWidth(double w) + { + if (origin == Origin.READ) + { + if (!initialized) + { + initialize(); + } + origin = Origin.READ_WRITE; + } + + width = w; + } + + /** + * Accessor for the height of this drawing + * + * @return the number of rows spanned by this image + */ + public double getHeight() + { + if (!initialized) + { + initialize(); + } + + return height; + } + + /** + * Accessor for the height of this drawing + * + * @param h the number of rows spanned by this image + */ + public void setHeight(double h) + { + if (origin == Origin.READ) + { + if (!initialized) + { + initialize(); + } + origin = Origin.READ_WRITE; + } + + height = h; + } + + + /** + * Gets the SpContainer that was read in + * + * @return the read sp container + */ + private EscherContainer getReadSpContainer() + { + if (!initialized) + { + initialize(); + } + + return readSpContainer; + } + + /** + * Accessor for the image data + * + * @return the image data + */ + public byte[] getImageData() + { + Assert.verify(origin == Origin.READ || origin == Origin.READ_WRITE); + + if (!initialized) + { + initialize(); + } + + return drawingGroup.getImageData(blipId); + } + + /** + * Accessor for the type + * + * @return the type + */ + public ShapeType getType() + { + return type; + } + + /** + * Sets the text object + * + * @param t the text object + */ + public void setTextObject(TextObjectRecord t) + { + txo = t; + } + + /** + * Sets the note object + * + * @param t the note record + */ + public void setNote(NoteRecord t) + { + note = t; + } + + /** + * Sets the text data + * + * @param t the text data + */ + public void setText(ContinueRecord t) + { + text = t; + } + + /** + * Sets the formatting + * + * @param t the formatting record + */ + public void setFormatting(ContinueRecord t) + { + formatting = t; + } + + /** + * Accessor for the image data + * + * @return the image data + */ + public byte[] getImageBytes() + { + Assert.verify(false); + return null; + } + + /** + * Accessor for the image file path. Normally this is the absolute path + * of a file on the directory system, but if this drawing was constructed + * using an byte[] then the blip id is returned + * + * @return the image file path, or the blip id + */ + public String getImageFilePath() + { + Assert.verify(false); + return null; + } + + /** + * Adds an mso record to this object + * + * @param d the mso record + */ + public void addMso(MsoDrawingRecord d) + { + mso = d; + drawingData.addRawData(mso.getData()); + } + + /** + * Writes out the additional comment records + * + * @param outputFile the output file + * @exception IOException + */ + public void writeAdditionalRecords(File outputFile) throws IOException + { + if (origin == Origin.READ) + { + outputFile.write(objRecord); + + if (mso != null) + { + outputFile.write(mso); + } + outputFile.write(txo); + outputFile.write(text); + if (formatting != null) + { + outputFile.write(formatting); + } + return; + } + + // Create the obj record + ObjRecord objrec = new ObjRecord(objectId, + ObjRecord.EXCELNOTE); + + outputFile.write(objrec); + + // Create the mso data record. Write the text box record again, + // although it is already included in the SpContainer + ClientTextBox textBox = new ClientTextBox(); + MsoDrawingRecord msod = new MsoDrawingRecord(textBox.getData()); + outputFile.write(msod); + + TextObjectRecord txorec = new TextObjectRecord(getText()); + outputFile.write(txorec); + + // Data for the first continue record + byte[] textData = new byte[commentText.length() * 2 + 1]; + textData[0] = 0x1; // unicode indicator + StringHelper.getUnicodeBytes(commentText, textData, 1); + //StringHelper.getBytes(commentText, textData, 1); + ContinueRecord textContinue = new ContinueRecord(textData); + outputFile.write(textContinue); + + // Data for the formatting runs + + byte[] frData = new byte[16]; + + // First txo run (the user) + IntegerHelper.getTwoBytes(0, frData, 0); // index to the first character + IntegerHelper.getTwoBytes(0, frData, 2); // index to the font(default) + // Mandatory last txo run + IntegerHelper.getTwoBytes(commentText.length(), frData, 8); + IntegerHelper.getTwoBytes(0, frData, 10); // index to the font(default) + + ContinueRecord frContinue = new ContinueRecord(frData); + outputFile.write(frContinue); + } + + /** + * Writes any records that need to be written after all the drawing group + * objects have been written + * Writes out all the note records + * + * @param outputFile the output file + * @exception IOException + */ + public void writeTailRecords(File outputFile) throws IOException + { + if (origin == Origin.READ) + { + outputFile.write(note); + return; + } + + // The note record + NoteRecord noteRecord = new NoteRecord(column, row, objectId); + outputFile.write(noteRecord); + } + + /** + * Accessor for the row + * + * @return the row + */ + public int getRow() + { + return note.getRow(); + } + + /** + * Accessor for the column + * + * @return the column + */ + public int getColumn() + { + return note.getColumn(); + } + + /** + * Accessor for the comment text + * + * @return the comment text + */ + public String getText() + { + if (commentText == null) + { + Assert.verify(text != null); + + byte[] td = text.getData(); + if (td[0] == 0) + { + commentText = StringHelper.getString + (td, td.length - 1, 1, workbookSettings); + } + else + { + commentText = StringHelper.getUnicodeString + (td, (td.length - 1) / 2, 1); + } + } + + return commentText; + } + + /** + * Hashing algorithm + * + * @return the hash code + */ + public int hashCode() + { + return commentText.hashCode(); + } + + /** + * Called when the comment text is changed during the sheet copy process + * + * @param t the new text + */ + public void setCommentText(String t) + { + commentText = t; + + if (origin == Origin.READ) + { + origin = Origin.READ_WRITE; + } + } + + /** + * Accessor for the first drawing on the sheet. This is used when + * copying unmodified sheets to indicate that this drawing contains + * the first time Escher gubbins + * + * @return TRUE if this MSORecord is the first drawing on the sheet + */ + public boolean isFirst() + { + return msoDrawingRecord.isFirst(); + } + + /** + * Queries whether this object is a form object. Form objects have their + * drawings records spread over several records and require special handling + * + * @return TRUE if this is a form object, FALSE otherwise + */ + public boolean isFormObject() + { + return true; + } +} + + + Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/Dg.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/Dg.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/Dg.java 17 Aug 2012 14:51:20 -0000 1.1 @@ -0,0 +1,101 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +import jxl.biff.IntegerHelper; + +/** + * The Drawing Group + */ +class Dg extends EscherAtom +{ + /** + * The data + */ + private byte[] data; + + /** + * The id of this drawing + */ + private int drawingId; + + /** + * The number of shapes + */ + private int shapeCount; + + /** + * The seed for drawing ids + */ + private int seed; + + /** + * Constructor invoked when reading in an escher stream + * + * @param erd the escher record + */ + public Dg(EscherRecordData erd) + { + super(erd); + drawingId = getInstance(); + + byte[] bytes = getBytes(); + shapeCount = IntegerHelper.getInt(bytes[0], bytes[1], bytes[2], bytes[3]); + seed = IntegerHelper.getInt(bytes[4], bytes[5], bytes[6], bytes[7]); + } + + /** + * Constructor invoked when writing out an escher stream + * + * @param numDrawings the number of drawings + */ + public Dg(int numDrawings) + { + super(EscherRecordType.DG); + drawingId = 1; + shapeCount = numDrawings + 1; + seed = 1024 + shapeCount + 1; + setInstance(drawingId); + } + + /** + * Gets the drawing id + * + * @return the drawing id + */ + public int getDrawingId() + { + return drawingId; + } + + /** + * Used to generate the drawing data + * + * @return the data + */ + byte[] getData() + { + data = new byte[8]; + IntegerHelper.getFourBytes(shapeCount, data, 0); + IntegerHelper.getFourBytes(seed, data, 4); + + return setHeaderData(data); + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/DgContainer.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/DgContainer.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/DgContainer.java 17 Aug 2012 14:51:20 -0000 1.1 @@ -0,0 +1,34 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +/** + * A Dg Container + */ +class DgContainer extends EscherContainer +{ + /** + * Constructor + */ + public DgContainer() + { + super(EscherRecordType.DG_CONTAINER); + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/Dgg.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/Dgg.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/Dgg.java 17 Aug 2012 14:51:21 -0000 1.1 @@ -0,0 +1,217 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +import java.util.ArrayList; + +import common.Logger; + +import jxl.biff.IntegerHelper; + +/** + * Dgg record + */ +class Dgg extends EscherAtom +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(Dgg.class); + + /** + * The binary data + */ + private byte[] data; + + /** + * The number of clusters + */ + private int numClusters; + + /** + * The maximum shape id + */ + private int maxShapeId; + + /** + * The number of shapes saved + */ + private int shapesSaved; + + /** + * The number of drawings saved + */ + private int drawingsSaved; + + /** + * The clusters + */ + private ArrayList clusters; + + /** + * The cluster structure + */ + static final class Cluster + { + /** + * The drawing group id + */ + int drawingGroupId; + + /** + * The something or other + */ + int shapeIdsUsed; + + /** + * Constructor + * + * @param dgId the drawing group id + * @param sids the sids + */ + Cluster(int dgId, int sids) + { + drawingGroupId = dgId; + shapeIdsUsed = sids; + } + } + + /** + * Constructor + * + * @param erd the read in data + */ + public Dgg(EscherRecordData erd) + { + super(erd); + clusters = new ArrayList(); + byte[] bytes = getBytes(); + maxShapeId = IntegerHelper.getInt + (bytes[0], bytes[1], bytes[2], bytes[3]); + numClusters = IntegerHelper.getInt + (bytes[4], bytes[5], bytes[6], bytes[7]); + shapesSaved = IntegerHelper.getInt + (bytes[8], bytes[9], bytes[10], bytes[11]); + drawingsSaved = IntegerHelper.getInt + (bytes[12], bytes[13], bytes[14], bytes[15]); + + int pos = 16; + for (int i = 0; i < numClusters; i++) + { + int dgId = IntegerHelper.getInt(bytes[pos], bytes[pos + 1]); + int sids = IntegerHelper.getInt(bytes[pos + 2], bytes[pos + 3]); + Cluster c = new Cluster(dgId, sids); + clusters.add(c); + pos += 4; + } + } + + /** + * Constructor + * + * @param numShapes the number of shapes + * @param numDrawings the number of drawings + */ + public Dgg(int numShapes, int numDrawings) + { + super(EscherRecordType.DGG); + shapesSaved = numShapes; + drawingsSaved = numDrawings; + clusters = new ArrayList(); + } + + /** + * Adds a cluster to this record + * + * @param dgid the id + * @param sids the sid + */ + void addCluster(int dgid, int sids) + { + Cluster c = new Cluster(dgid, sids); + clusters.add(c); + } + + /** + * Gets the data for writing out + * + * @return the binary data + */ + byte[] getData() + { + numClusters = clusters.size(); + data = new byte[16 + numClusters * 4]; + + // The max shape id + IntegerHelper.getFourBytes(1024 + shapesSaved, data, 0); + + // The number of clusters + IntegerHelper.getFourBytes(numClusters, data, 4); + + // The number of shapes saved + IntegerHelper.getFourBytes(shapesSaved, data, 8); + + // The number of drawings saved + // IntegerHelper.getFourBytes(drawingsSaved, data, 12); + IntegerHelper.getFourBytes(1, data, 12); + + int pos = 16; + for (int i = 0; i < numClusters; i++) + { + Cluster c = (Cluster) clusters.get(i); + IntegerHelper.getTwoBytes(c.drawingGroupId, data, pos); + IntegerHelper.getTwoBytes(c.shapeIdsUsed, data, pos + 2); + pos += 4; + } + + return setHeaderData(data); + } + + /** + * Accessor for the number of shapes saved + * + * @return the number of shapes saved + */ + int getShapesSaved() + { + return shapesSaved; + } + + /** + * Accessor for the number of drawings saved + * + * @return the number of drawings saved + */ + int getDrawingsSaved() + { + return drawingsSaved; + } + + /** + * Accessor for a particular cluster + * + * @param i the cluster number + * @return the cluster + */ + Cluster getCluster(int i) + { + return (Cluster) clusters.get(i); + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/DggContainer.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/DggContainer.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/DggContainer.java 17 Aug 2012 14:51:19 -0000 1.1 @@ -0,0 +1,34 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +/** + * Container for Dgg objects + */ +class DggContainer extends EscherContainer +{ + /** + * Constructor + */ + public DggContainer() + { + super(EscherRecordType.DGG_CONTAINER); + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/Drawing.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/Drawing.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/Drawing.java 17 Aug 2012 14:51:20 -0000 1.1 @@ -0,0 +1,1130 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +import java.io.FileInputStream; +import java.io.IOException; + +import common.Assert; +import common.Logger; +import common.LengthUnit; +import common.LengthConverter; + +import jxl.Image; +import jxl.Sheet; +import jxl.CellView; +import jxl.write.biff.File; + + +/** + * Contains the various biff records used to insert a drawing into a + * worksheet + */ +public class Drawing implements DrawingGroupObject, Image +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(Drawing.class); + + /** + * The spContainer that was read in + */ + private EscherContainer readSpContainer; + + /** + * The MsoDrawingRecord associated with the drawing + */ + private MsoDrawingRecord msoDrawingRecord; + + /** + * The ObjRecord associated with the drawing + */ + private ObjRecord objRecord; + + /** + * Initialized flag + */ + private boolean initialized = false; + + /** + * The file containing the image + */ + private java.io.File imageFile; + + /** + * The raw image data, used instead of an image file + */ + private byte[] imageData; + + /** + * The object id, assigned by the drawing group + */ + private int objectId; + + /** + * The blip id + */ + private int blipId; + + /** + * The column position of the image + */ + private double x; + + /** + * The row position of the image + */ + private double y; + + /** + * The width of the image in cells + */ + private double width; + + /** + * The height of the image in cells + */ + private double height; + + /** + * The number of places this drawing is referenced + */ + private int referenceCount; + + /** + * The top level escher container + */ + private EscherContainer escherData; + + /** + * Where this image came from (read, written or a copy) + */ + private Origin origin; + + /** + * The drawing group for all the images + */ + private DrawingGroup drawingGroup; + + /** + * The drawing data + */ + private DrawingData drawingData; + + /** + * The type of this drawing object + */ + private ShapeType type; + + /** + * The shape id + */ + private int shapeId; + + /** + * The drawing position on the sheet + */ + private int drawingNumber; + + /** + * A reference to the sheet containing this drawing. Used to calculate + * the drawing dimensions in pixels + */ + private Sheet sheet; + + /** + * Reader for the raw image data + */ + private PNGReader pngReader; + + /** + * The client anchor properties + */ + private ImageAnchorProperties imageAnchorProperties; + + // Enumeration type for the image anchor properties + protected static class ImageAnchorProperties + { + private int value; + private static ImageAnchorProperties[] o = new ImageAnchorProperties[0]; + + ImageAnchorProperties(int v) + { + value = v; + + ImageAnchorProperties[] oldArray = o; + o = new ImageAnchorProperties[oldArray.length + 1]; + System.arraycopy(oldArray, 0, o, 0, oldArray.length); + o[oldArray.length] = this; + } + + int getValue() + { + return value; + } + + static ImageAnchorProperties getImageAnchorProperties(int val) + { + ImageAnchorProperties iap = MOVE_AND_SIZE_WITH_CELLS; + int pos = 0; + while (pos < o.length) + { + if (o[pos].getValue()== val) + { + iap = o[pos]; + break; + } + else + { + pos++; + } + } + return iap; + } + } + + // The image anchor properties + public static ImageAnchorProperties MOVE_AND_SIZE_WITH_CELLS = + new ImageAnchorProperties(1); + public static ImageAnchorProperties MOVE_WITH_CELLS = + new ImageAnchorProperties(2); + public static ImageAnchorProperties NO_MOVE_OR_SIZE_WITH_CELLS = + new ImageAnchorProperties(3); + + /** + * The default font size for columns + */ + private static final double DEFAULT_FONT_SIZE = 10; + + /** + * Constructor used when reading images + * + * @param mso the drawing record + * @param obj the object record + * @param dd the drawing data for all drawings on this sheet + * @param dg the drawing group + */ + public Drawing(MsoDrawingRecord mso, + ObjRecord obj, + DrawingData dd, + DrawingGroup dg, + Sheet s) + { + drawingGroup = dg; + msoDrawingRecord = mso; + drawingData = dd; + objRecord = obj; + sheet = s; + initialized = false; + origin = Origin.READ; + drawingData.addData(msoDrawingRecord.getData()); + drawingNumber = drawingData.getNumDrawings() - 1; + drawingGroup.addDrawing(this); + + Assert.verify(mso != null && obj != null); + + initialize(); + } + + /** + * Copy constructor used to copy drawings from read to write + * + * @param dgo the drawing group object + * @param dg the drawing group + */ + protected Drawing(DrawingGroupObject dgo, DrawingGroup dg) + { + Drawing d = (Drawing) dgo; + Assert.verify(d.origin == Origin.READ); + msoDrawingRecord = d.msoDrawingRecord; + objRecord = d.objRecord; + initialized = false; + origin = Origin.READ; + drawingData = d.drawingData; + drawingGroup = dg; + drawingNumber = d.drawingNumber; + drawingGroup.addDrawing(this); + } + + /** + * Constructor invoked when writing the images + * + * @param x the column + * @param y the row + * @param w the width in cells + * @param h the height in cells + * @param image the image file + */ + public Drawing(double x, + double y, + double w, + double h, + java.io.File image) + { + imageFile = image; + initialized = true; + origin = Origin.WRITE; + this.x = x; + this.y = y; + this.width = w; + this.height = h; + referenceCount = 1; + imageAnchorProperties = MOVE_WITH_CELLS; + type = ShapeType.PICTURE_FRAME; + } + + /** + * Constructor invoked when writing the images + * + * @param x the column + * @param y the row + * @param w the width in cells + * @param h the height in cells + * @param image the image data + */ + public Drawing(double x, + double y, + double w, + double h, + byte[] image) + { + imageData = image; + initialized = true; + origin = Origin.WRITE; + this.x = x; + this.y = y; + this.width = w; + this.height = h; + referenceCount = 1; + imageAnchorProperties = MOVE_WITH_CELLS; + type = ShapeType.PICTURE_FRAME; + } + + /** + * Initializes the member variables from the Escher stream data + */ + private void initialize() + { + readSpContainer = drawingData.getSpContainer(drawingNumber); + Assert.verify(readSpContainer != null); + + EscherRecord[] children = readSpContainer.getChildren(); + + Sp sp = (Sp) readSpContainer.getChildren()[0]; + shapeId = sp.getShapeId(); + objectId = objRecord.getObjectId(); + type = ShapeType.getType(sp.getShapeType()); + + if (type == ShapeType.UNKNOWN) + { + logger.warn("Unknown shape type"); + } + + Opt opt = (Opt) readSpContainer.getChildren()[1]; + + if (opt.getProperty(260) != null) + { + blipId = opt.getProperty(260).value; + } + + if (opt.getProperty(261) != null) + { + imageFile = new java.io.File(opt.getProperty(261).stringValue); + } + else + { + if (type == ShapeType.PICTURE_FRAME) + { + logger.warn("no filename property for drawing"); + imageFile = new java.io.File(Integer.toString(blipId)); + } + } + + ClientAnchor clientAnchor = null; + for (int i = 0; i < children.length && clientAnchor == null; i++) + { + if (children[i].getType() == EscherRecordType.CLIENT_ANCHOR) + { + clientAnchor = (ClientAnchor) children[i]; + } + } + + if (clientAnchor == null) + { + logger.warn("client anchor not found"); + } + else + { + x = clientAnchor.getX1(); + y = clientAnchor.getY1(); + width = clientAnchor.getX2() - x; + height = clientAnchor.getY2() - y; + imageAnchorProperties = ImageAnchorProperties.getImageAnchorProperties + (clientAnchor.getProperties()); + } + + if (blipId == 0) + { + logger.warn("linked drawings are not supported"); + } + + initialized = true; + } + + /** + * Accessor for the image file + * + * @return the image file + */ + public java.io.File getImageFile() + { + return imageFile; + } + + /** + * Accessor for the image file path. Normally this is the absolute path + * of a file on the directory system, but if this drawing was constructed + * using an byte[] then the blip id is returned + * + * @return the image file path, or the blip id + */ + public String getImageFilePath() + { + if (imageFile == null) + { + // return the blip id, if it exists + return blipId != 0 ? Integer.toString(blipId) : "__new__image__"; + } + + return imageFile.getPath(); + } + + /** + * Sets the object id. Invoked by the drawing group when the object is + * added to id + * + * @param objid the object id + * @param bip the blip id + * @param sid the shape id + */ + public final void setObjectId(int objid, int bip, int sid) + { + objectId = objid; + blipId = bip; + shapeId = sid; + + if (origin == Origin.READ) + { + origin = Origin.READ_WRITE; + } + } + + /** + * Accessor for the object id + * + * @return the object id + */ + public final int getObjectId() + { + if (!initialized) + { + initialize(); + } + + return objectId; + } + + /** + * Accessor for the shape id + * + * @return the shape id + */ + public int getShapeId() + { + if (!initialized) + { + initialize(); + } + + return shapeId; + } + + /** + * Accessor for the blip id + * + * @return the blip id + */ + public final int getBlipId() + { + if (!initialized) + { + initialize(); + } + + return blipId; + } + + /** + * Gets the drawing record which was read in + * + * @return the drawing record + */ + public MsoDrawingRecord getMsoDrawingRecord() + { + return msoDrawingRecord; + } + + /** + * Creates the main Sp container for the drawing + * + * @return the SP container + */ + public EscherContainer getSpContainer() + { + if (!initialized) + { + initialize(); + } + + if (origin == Origin.READ) + { + return getReadSpContainer(); + } + + SpContainer spContainer = new SpContainer(); + Sp sp = new Sp(type, shapeId, 2560); + spContainer.add(sp); + Opt opt = new Opt(); + opt.addProperty(260, true, false, blipId); + + if (type == ShapeType.PICTURE_FRAME) + { + String filePath = imageFile != null ? imageFile.getPath() : ""; + opt.addProperty(261, true, true, filePath.length() * 2, filePath); + opt.addProperty(447, false, false, 65536); + opt.addProperty(959, false, false, 524288); + spContainer.add(opt); + } + + ClientAnchor clientAnchor = new ClientAnchor + (x, y, x + width, y + height, + imageAnchorProperties.getValue()); + spContainer.add(clientAnchor); + ClientData clientData = new ClientData(); + spContainer.add(clientData); + + return spContainer; + } + + /** + * Sets the drawing group for this drawing. Called by the drawing group + * when this drawing is added to it + * + * @param dg the drawing group + */ + public void setDrawingGroup(DrawingGroup dg) + { + drawingGroup = dg; + } + + /** + * Accessor for the drawing group + * + * @return the drawing group + */ + public DrawingGroup getDrawingGroup() + { + return drawingGroup; + } + + /** + * Gets the origin of this drawing + * + * @return where this drawing came from + */ + public Origin getOrigin() + { + return origin; + } + + /** + * Accessor for the reference count on this drawing + * + * @return the reference count + */ + public int getReferenceCount() + { + return referenceCount; + } + + /** + * Sets the new reference count on the drawing + * + * @param r the new reference count + */ + public void setReferenceCount(int r) + { + referenceCount = r; + } + + /** + * Accessor for the column of this drawing + * + * @return the column + */ + public double getX() + { + if (!initialized) + { + initialize(); + } + return x; + } + + /** + * Sets the column position of this drawing + * + * @param x the column + */ + public void setX(double x) + { + if (origin == Origin.READ) + { + if (!initialized) + { + initialize(); + } + origin = Origin.READ_WRITE; + } + + this.x = x; + } + + /** + * Accessor for the row of this drawing + * + * @return the row + */ + public double getY() + { + if (!initialized) + { + initialize(); + } + + return y; + } + + /** + * Accessor for the row of the drawing + * + * @param y the row + */ + public void setY(double y) + { + if (origin == Origin.READ) + { + if (!initialized) + { + initialize(); + } + origin = Origin.READ_WRITE; + } + + this.y = y; + } + + + /** + * Accessor for the width of this drawing + * + * @return the number of columns spanned by this image + */ + public double getWidth() + { + if (!initialized) + { + initialize(); + } + + return width; + } + + /** + * Accessor for the width + * + * @param w the number of columns to span + */ + public void setWidth(double w) + { + if (origin == Origin.READ) + { + if (!initialized) + { + initialize(); + } + origin = Origin.READ_WRITE; + } + + width = w; + } + + /** + * Accessor for the height of this drawing + * + * @return the number of rows spanned by this image + */ + public double getHeight() + { + if (!initialized) + { + initialize(); + } + + return height; + } + + /** + * Accessor for the height of this drawing + * + * @param h the number of rows spanned by this image + */ + public void setHeight(double h) + { + if (origin == Origin.READ) + { + if (!initialized) + { + initialize(); + } + origin = Origin.READ_WRITE; + } + + height = h; + } + + + /** + * Gets the SpContainer that was read in + * + * @return the read sp container + */ + private EscherContainer getReadSpContainer() + { + if (!initialized) + { + initialize(); + } + + return readSpContainer; + } + + /** + * Accessor for the image data + * + * @return the image data + */ + public byte[] getImageData() + { + Assert.verify(origin == Origin.READ || origin == Origin.READ_WRITE); + + if (!initialized) + { + initialize(); + } + + return drawingGroup.getImageData(blipId); + } + + /** + * Accessor for the image data + * + * @return the image data + */ + public byte[] getImageBytes() throws IOException + { + if (origin == Origin.READ || origin == Origin.READ_WRITE) + { + return getImageData(); + } + + Assert.verify(origin == Origin.WRITE); + + if (imageFile == null) + { + Assert.verify(imageData != null); + return imageData; + } + + byte[] data = new byte[(int) imageFile.length()]; + FileInputStream fis = new FileInputStream(imageFile); + fis.read(data, 0, data.length); + fis.close(); + return data; + } + + /** + * Accessor for the type + * + * @return the type + */ + public ShapeType getType() + { + return type; + } + + /** + * Writes any other records associated with this drawing group object + * + * @param outputFile the output file + * @exception IOException + */ + public void writeAdditionalRecords(File outputFile) throws IOException + { + if (origin == Origin.READ) + { + outputFile.write(objRecord); + return; + } + + // Create the obj record + ObjRecord objrec = new ObjRecord(objectId, + ObjRecord.PICTURE); + outputFile.write(objrec); + } + + /** + * Writes any records that need to be written after all the drawing group + * objects have been written + * Does nothing here + * + * @param outputFile the output file + */ + public void writeTailRecords(File outputFile) throws IOException + { + // does nothing + } + + /** + * Interface method + * + * @return the column number at which the image is positioned + */ + public double getColumn() + { + return getX(); + } + + /** + * Interface method + * + * @return the row number at which the image is positions + */ + public double getRow() + { + return getY(); + } + + /** + * Accessor for the first drawing on the sheet. This is used when + * copying unmodified sheets to indicate that this drawing contains + * the first time Escher gubbins + * + * @return TRUE if this MSORecord is the first drawing on the sheet + */ + public boolean isFirst() + { + return msoDrawingRecord.isFirst(); + } + + /** + * Queries whether this object is a form object. Form objects have their + * drawings records spread over TXO and CONTINUE records and + * require special handling + * + * @return TRUE if this is a form object, FALSE otherwise + */ + public boolean isFormObject() + { + return false; + } + + /** + * Removes a row + * + * @param r the row to be removed + */ + public void removeRow(int r) + { + if (y > r) + { + setY(r); + } + } + + /** + * Accessor for the image dimensions. See technotes for Bill's explanation + * of the calculation logic + * + * @return approximate drawing size in pixels + */ + private double getWidthInPoints() + { + if (sheet == null) + { + logger.warn("calculating image width: sheet is null"); + return 0; + } + + // The start and end row numbers + int firstCol = (int) x; + int lastCol = (int) Math.ceil(x + width) - 1; + + // **** MAGIC NUMBER ALERT *** + // multiply the point size of the font by 0.59 to give the point size + // I know of no explanation for this yet, other than that it seems to + // give the right answer + + // Get the width of the image within the first col, allowing for + // fractional offsets + CellView cellView = sheet.getColumnView(firstCol); + int firstColWidth = cellView.getSize(); + double firstColImageWidth = (1 - (x - firstCol)) * firstColWidth; + double pointSize = (cellView.getFormat() != null) ? + cellView.getFormat().getFont().getPointSize() : DEFAULT_FONT_SIZE; + double firstColWidthInPoints = firstColImageWidth * 0.59 * pointSize / 256; + + // Get the height of the image within the last row, allowing for + // fractional offsets + int lastColWidth = 0; + double lastColImageWidth = 0; + double lastColWidthInPoints = 0; + if (lastCol != firstCol) + { + cellView = sheet.getColumnView(lastCol); + lastColWidth = cellView.getSize(); + lastColImageWidth = (x + width - lastCol) * lastColWidth; + pointSize = (cellView.getFormat() != null) ? + cellView.getFormat().getFont().getPointSize() : DEFAULT_FONT_SIZE; + lastColWidthInPoints = lastColImageWidth * 0.59 * pointSize / 256; + } + + // Now get all the columns in between + double width = 0; + for (int i = 0 ; i < lastCol - firstCol - 1 ; i++) + { + cellView = sheet.getColumnView(firstCol + 1 +i); + pointSize = (cellView.getFormat() != null) ? + cellView.getFormat().getFont().getPointSize() : DEFAULT_FONT_SIZE; + width += cellView.getSize() * 0.59 * pointSize / 256; + } + + // Add on the first and last row contributions to get the height in twips + double widthInPoints = width + + firstColWidthInPoints + lastColWidthInPoints; + + return widthInPoints; + } + + /** + * Accessor for the image dimensions. See technotes for Bill's explanation + * of the calculation logic + * + * @return approximate drawing size in pixels + */ + private double getHeightInPoints() + { + if (sheet == null) + { + logger.warn("calculating image height: sheet is null"); + return 0; + } + + // The start and end row numbers + int firstRow = (int) y; + int lastRow = (int) Math.ceil(y + height) - 1; + + // Get the height of the image within the first row, allowing for + // fractional offsets + int firstRowHeight = sheet.getRowView(firstRow).getSize(); + double firstRowImageHeight = (1 - (y - firstRow)) * firstRowHeight; + + // Get the height of the image within the last row, allowing for + // fractional offsets + int lastRowHeight = 0; + double lastRowImageHeight = 0; + if (lastRow != firstRow) + { + lastRowHeight = sheet.getRowView(lastRow).getSize(); + lastRowImageHeight = (y + height - lastRow) * lastRowHeight; + } + + // Now get all the rows in between + double height = 0; + for (int i = 0 ; i < lastRow - firstRow - 1 ; i++) + { + height += sheet.getRowView(firstRow + 1 + i).getSize(); + } + + // Add on the first and last row contributions to get the height in twips + double heightInTwips = height + firstRowHeight + lastRowHeight; + + // Now divide by the magic number to converts twips into pixels and + // return the value + double heightInPoints = heightInTwips / 20.0; + + return heightInPoints; + } + + /** + * Get the width of this image as rendered within Excel + * + * @param unit the unit of measurement + * @return the width of the image within Excel + */ + public double getWidth(LengthUnit unit) + { + double widthInPoints = getWidthInPoints(); + return widthInPoints * LengthConverter.getConversionFactor + (LengthUnit.POINTS, unit); + } + + /** + * Get the height of this image as rendered within Excel + * + * @param unit the unit of measurement + * @return the height of the image within Excel + */ + public double getHeight(LengthUnit unit) + { + double heightInPoints = getHeightInPoints(); + return heightInPoints * LengthConverter.getConversionFactor + (LengthUnit.POINTS, unit); + } + + /** + * Gets the width of the image. Note that this is the width of the + * underlying image, and does not take into account any size manipulations + * that may have occurred when the image was added into Excel + * + * @return the image width in pixels + */ + public int getImageWidth() + { + return getPngReader().getWidth(); + } + + /** + * Gets the height of the image. Note that this is the height of the + * underlying image, and does not take into account any size manipulations + * that may have occurred when the image was added into Excel + * + * @return the image width in pixels + */ + public int getImageHeight() + { + return getPngReader().getHeight(); + } + + + /** + * Gets the horizontal resolution of the image, if that information + * is available. + * + * @return the number of dots per unit specified, if available, 0 otherwise + */ + public double getHorizontalResolution(LengthUnit unit) + { + int res = getPngReader().getHorizontalResolution(); + return res / LengthConverter.getConversionFactor(LengthUnit.METRES, unit); + } + + /** + * Gets the vertical resolution of the image, if that information + * is available. + * + * @return the number of dots per unit specified, if available, 0 otherwise + */ + public double getVerticalResolution(LengthUnit unit) + { + int res = getPngReader().getVerticalResolution(); + return res / LengthConverter.getConversionFactor(LengthUnit.METRES, unit); + } + + private PNGReader getPngReader() + { + if (pngReader != null) + { + return pngReader; + } + + byte[] imdata = null; + if (origin == Origin.READ || origin == Origin.READ_WRITE) + { + imdata = getImageData(); + } + else + { + try + { + imdata = getImageBytes(); + } + catch (IOException e) + { + logger.warn("Could not read image file"); + imdata = new byte[0]; + } + } + + pngReader = new PNGReader(imdata); + pngReader.read(); + return pngReader; + } + + /** + * Accessor for the anchor properties + */ + protected void setImageAnchor(ImageAnchorProperties iap) + { + imageAnchorProperties = iap; + + if (origin == Origin.READ) + { + origin = Origin.READ_WRITE; + } + } + + /** + * Accessor for the anchor properties + */ + protected ImageAnchorProperties getImageAnchor() + { + if (!initialized) + { + initialize(); + } + + return imageAnchorProperties; + } +} + + + Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/Drawing2.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/Drawing2.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/Drawing2.java 17 Aug 2012 14:51:20 -0000 1.1 @@ -0,0 +1,690 @@ +/********************************************************************* +* +* Copyright (C) 2006 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +import java.io.FileInputStream; +import java.io.IOException; + +import common.Assert; +import common.Logger; + +import jxl.write.biff.File; + + +/** + * Contains the various biff records used to insert a drawing into a + * worksheet. This type of image does not have an associated object + * record + */ +public class Drawing2 implements DrawingGroupObject +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(Drawing.class); + + /** + * The spContainer that was read in + */ + private EscherContainer readSpContainer; + + /** + * The MsoDrawingRecord associated with the drawing + */ + private MsoDrawingRecord msoDrawingRecord; + + /** + * Initialized flag + */ + private boolean initialized = false; + + /** + * The file containing the image + */ + private java.io.File imageFile; + + /** + * The raw image data, used instead of an image file + */ + private byte[] imageData; + + /** + * The object id, assigned by the drawing group + */ + private int objectId; + + /** + * The blip id + */ + private int blipId; + + /** + * The column position of the image + */ + private double x; + + /** + * The row position of the image + */ + private double y; + + /** + * The width of the image in cells + */ + private double width; + + /** + * The height of the image in cells + */ + private double height; + + /** + * The number of places this drawing is referenced + */ + private int referenceCount; + + /** + * The top level escher container + */ + private EscherContainer escherData; + + /** + * Where this image came from (read, written or a copy) + */ + private Origin origin; + + /** + * The drawing group for all the images + */ + private DrawingGroup drawingGroup; + + /** + * The drawing data + */ + private DrawingData drawingData; + + /** + * The type of this drawing object + */ + private ShapeType type; + + /** + * The shape id + */ + private int shapeId; + + /** + * The drawing position on the sheet + */ + private int drawingNumber; + + + /** + * Constructor used when reading images + * + * @param mso the drawing record + * @param dd the drawing data for all drawings on this sheet + * @param dg the drawing group + */ + public Drawing2(MsoDrawingRecord mso, + DrawingData dd, + DrawingGroup dg) + { + drawingGroup = dg; + msoDrawingRecord = mso; + drawingData = dd; + initialized = false; + origin = Origin.READ; + // there is no drawing number associated with this drawing + drawingData.addRawData(msoDrawingRecord.getData()); + drawingGroup.addDrawing(this); + + Assert.verify(mso != null); + + initialize(); + } + + /** + * Copy constructor used to copy drawings from read to write + * + * @param dgo the drawing group object + * @param dg the drawing group + */ + protected Drawing2(DrawingGroupObject dgo, DrawingGroup dg) + { + Drawing2 d = (Drawing2) dgo; + Assert.verify(d.origin == Origin.READ); + msoDrawingRecord = d.msoDrawingRecord; + initialized = false; + origin = Origin.READ; + drawingData = d.drawingData; + drawingGroup = dg; + drawingNumber = d.drawingNumber; + drawingGroup.addDrawing(this); + } + + /** + * Constructor invoked when writing the images + * + * @param x the column + * @param y the row + * @param w the width in cells + * @param h the height in cells + * @param image the image file + */ + public Drawing2(double x, + double y, + double w, + double h, + java.io.File image) + { + imageFile = image; + initialized = true; + origin = Origin.WRITE; + this.x = x; + this.y = y; + this.width = w; + this.height = h; + referenceCount = 1; + type = ShapeType.PICTURE_FRAME; + } + + /** + * Constructor invoked when writing the images + * + * @param x the column + * @param y the row + * @param w the width in cells + * @param h the height in cells + * @param image the image data + */ + public Drawing2(double x, + double y, + double w, + double h, + byte[] image) + { + imageData = image; + initialized = true; + origin = Origin.WRITE; + this.x = x; + this.y = y; + this.width = w; + this.height = h; + referenceCount = 1; + type = ShapeType.PICTURE_FRAME; + } + + /** + * Initializes the member variables from the Escher stream data + */ + private void initialize() + { + initialized = true; + } + + /** + * Sets the object id. Invoked by the drawing group when the object is + * added to id + * + * @param objid the object id + * @param bip the blip id + * @param sid the shape id + */ + public final void setObjectId(int objid, int bip, int sid) + { + objectId = objid; + blipId = bip; + shapeId = sid; + + if (origin == Origin.READ) + { + origin = Origin.READ_WRITE; + } + } + + /** + * Accessor for the object id + * + * @return the object id + */ + public final int getObjectId() + { + if (!initialized) + { + initialize(); + } + + return objectId; + } + + /** + * Accessor for the shape id + * + * @return the shape id + */ + public int getShapeId() + { + if (!initialized) + { + initialize(); + } + + return shapeId; + } + + /** + * Accessor for the blip id + * + * @return the blip id + */ + public final int getBlipId() + { + if (!initialized) + { + initialize(); + } + + return blipId; + } + + /** + * Gets the drawing record which was read in + * + * @return the drawing record + */ + public MsoDrawingRecord getMsoDrawingRecord() + { + return msoDrawingRecord; + } + + /** + * Creates the main Sp container for the drawing + * + * @return the SP container + */ + public EscherContainer getSpContainer() + { + if (!initialized) + { + initialize(); + } + + Assert.verify(origin == Origin.READ); + + return getReadSpContainer(); + } + + /** + * Sets the drawing group for this drawing. Called by the drawing group + * when this drawing is added to it + * + * @param dg the drawing group + */ + public void setDrawingGroup(DrawingGroup dg) + { + drawingGroup = dg; + } + + /** + * Accessor for the drawing group + * + * @return the drawing group + */ + public DrawingGroup getDrawingGroup() + { + return drawingGroup; + } + + /** + * Gets the origin of this drawing + * + * @return where this drawing came from + */ + public Origin getOrigin() + { + return origin; + } + + /** + * Accessor for the reference count on this drawing + * + * @return the reference count + */ + public int getReferenceCount() + { + return referenceCount; + } + + /** + * Sets the new reference count on the drawing + * + * @param r the new reference count + */ + public void setReferenceCount(int r) + { + referenceCount = r; + } + + /** + * Accessor for the column of this drawing + * + * @return the column + */ + public double getX() + { + if (!initialized) + { + initialize(); + } + return x; + } + + /** + * Sets the column position of this drawing + * + * @param x the column + */ + public void setX(double x) + { + if (origin == Origin.READ) + { + if (!initialized) + { + initialize(); + } + origin = Origin.READ_WRITE; + } + + this.x = x; + } + + /** + * Accessor for the row of this drawing + * + * @return the row + */ + public double getY() + { + if (!initialized) + { + initialize(); + } + + return y; + } + + /** + * Accessor for the row of the drawing + * + * @param y the row + */ + public void setY(double y) + { + if (origin == Origin.READ) + { + if (!initialized) + { + initialize(); + } + origin = Origin.READ_WRITE; + } + + this.y = y; + } + + + /** + * Accessor for the width of this drawing + * + * @return the number of columns spanned by this image + */ + public double getWidth() + { + if (!initialized) + { + initialize(); + } + + return width; + } + + /** + * Accessor for the width + * + * @param w the number of columns to span + */ + public void setWidth(double w) + { + if (origin == Origin.READ) + { + if (!initialized) + { + initialize(); + } + origin = Origin.READ_WRITE; + } + + width = w; + } + + /** + * Accessor for the height of this drawing + * + * @return the number of rows spanned by this image + */ + public double getHeight() + { + if (!initialized) + { + initialize(); + } + + return height; + } + + /** + * Accessor for the height of this drawing + * + * @param h the number of rows spanned by this image + */ + public void setHeight(double h) + { + if (origin == Origin.READ) + { + if (!initialized) + { + initialize(); + } + origin = Origin.READ_WRITE; + } + + height = h; + } + + + /** + * Gets the SpContainer that was read in + * + * @return the read sp container + */ + private EscherContainer getReadSpContainer() + { + if (!initialized) + { + initialize(); + } + + return readSpContainer; + } + + /** + * Accessor for the image data + * + * @return the image data + */ + public byte[] getImageData() + { + Assert.verify(false); + Assert.verify(origin == Origin.READ || origin == Origin.READ_WRITE); + + if (!initialized) + { + initialize(); + } + + return drawingGroup.getImageData(blipId); + } + + /** + * Accessor for the image data + * + * @return the image data + */ + public byte[] getImageBytes() throws IOException + { + Assert.verify(false); + if (origin == Origin.READ || origin == Origin.READ_WRITE) + { + return getImageData(); + } + + Assert.verify(origin == Origin.WRITE); + + if (imageFile == null) + { + Assert.verify(imageData != null); + return imageData; + } + + byte[] data = new byte[(int) imageFile.length()]; + FileInputStream fis = new FileInputStream(imageFile); + fis.read(data, 0, data.length); + fis.close(); + return data; + } + + /** + * Accessor for the type + * + * @return the type + */ + public ShapeType getType() + { + return type; + } + + /** + * Writes any other records associated with this drawing group object + * + * @param outputFile the output file + * @exception IOException + */ + public void writeAdditionalRecords(File outputFile) throws IOException + { + // no records to write + } + + /** + * Writes any records that need to be written after all the drawing group + * objects have been written + * Does nothing here + * + * @param outputFile the output file + * @exception IOException + */ + public void writeTailRecords(File outputFile) throws IOException + { + // does nothing + } + + /** + * Interface method + * + * @return the column number at which the image is positioned + */ + public double getColumn() + { + return getX(); + } + + /** + * Interface method + * + * @return the row number at which the image is positions + */ + public double getRow() + { + return getY(); + } + + /** + * Accessor for the first drawing on the sheet. This is used when + * copying unmodified sheets to indicate that this drawing contains + * the first time Escher gubbins + * + * @return TRUE if this MSORecord is the first drawing on the sheet + */ + public boolean isFirst() + { + return msoDrawingRecord.isFirst(); + } + + /** + * Queries whether this object is a form object. Form objects have their + * drawings records spread over TXO and CONTINUE records and + * require special handling + * + * @return TRUE if this is a form object, FALSE otherwise + */ + public boolean isFormObject() + { + return false; + } + + /** + * Removes a row + * + * @param r the row to be removed + */ + public void removeRow(int r) + { + if (y > r) + { + setY(r); + } + } + + /** + * Accessor for the image file path. Normally this is the absolute path + * of a file on the directory system, but if this drawing was constructed + * using an byte[] then the blip id is returned + * + * @return the image file path, or the blip id + */ + public String getImageFilePath() + { + Assert.verify(false); + return null; + } +} + + + Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/DrawingData.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/DrawingData.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/DrawingData.java 17 Aug 2012 14:51:19 -0000 1.1 @@ -0,0 +1,232 @@ +/********************************************************************* +* +* Copyright (C) 2004 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +import java.util.ArrayList; + +import common.Assert; +import common.Logger; + +/** + * Class used to concatenate all the data for the various drawing objects + * into one continuous stream + */ +public class DrawingData implements EscherStream +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(DrawingData.class); + + /** + * The drawing data + */ + private byte[] drawingData; + + /** + * The number of drawings + */ + private int numDrawings; + + /** + * Initialized flag + */ + private boolean initialized; + + /** + * The spgr container. The contains the SpContainer for each drawing + */ + private EscherRecord[] spContainers; + + /** + * Constructor + */ + public DrawingData() + { + numDrawings = 0; + drawingData = null; + initialized = false; + } + + /** + * Initialization + */ + private void initialize() + { + EscherRecordData er = new EscherRecordData(this, 0); + Assert.verify(er.isContainer()); + + EscherContainer dgContainer = new EscherContainer(er); + EscherRecord[] children = dgContainer.getChildren(); + + children = dgContainer.getChildren(); + // Dg dg = (Dg) children[0]; + + EscherContainer spgrContainer = null; + + for (int i = 0; i < children.length && spgrContainer == null; i++) + { + EscherRecord child = children[i]; + if (child.getType() == EscherRecordType.SPGR_CONTAINER) + { + spgrContainer = (EscherContainer) child; + } + } + Assert.verify(spgrContainer != null); + + EscherRecord[] spgrChildren = spgrContainer.getChildren(); + + // See if any of the spgrChildren are SpgrContainer + boolean nestedContainers = false; + for (int i = 0; i < spgrChildren.length && !nestedContainers; i++) + { + if (spgrChildren[i].getType() == EscherRecordType.SPGR_CONTAINER) + { + nestedContainers = true; + } + } + + // If there are no nested containers, simply set the spContainer list + // to be the list of children + if (!nestedContainers) + { + spContainers = spgrChildren; + } + else + { + // Go through the hierarchy and dig out all the Sp containers + ArrayList sps = new ArrayList(); + getSpContainers(spgrContainer, sps); + spContainers = new EscherRecord[sps.size()]; + spContainers = (EscherRecord[]) sps.toArray(spContainers); + } + + initialized = true; + } + + /** + * Gets the sp container from the internal data + * + * @param spgrContainer the spgr container + * @param sps the list of sp records + */ + private void getSpContainers(EscherContainer spgrContainer, ArrayList sps) + { + EscherRecord[] spgrChildren = spgrContainer.getChildren(); + for (int i = 0; i < spgrChildren.length; i++) + { + if (spgrChildren[i].getType() == EscherRecordType.SP_CONTAINER) + { + sps.add(spgrChildren[i]); + } + else if (spgrChildren[i].getType() == EscherRecordType.SPGR_CONTAINER) + { + getSpContainers((EscherContainer) spgrChildren[i], sps); + } + else + { + logger.warn("Spgr Containers contains a record other than Sp/Spgr " + + "containers"); + } + } + } + + /** + * Adds the byte stream to the drawing data + * + * @param data the data to add + */ + public void addData(byte[] data) + { + addRawData(data); + numDrawings++; + } + + /** + * Adds the data to the array without incrementing the drawing number. + * This is used by comments, which for some bizarre and inexplicable + * reason split out the data + * + * @param data the data to add + */ + public void addRawData(byte[] data) + { + if (drawingData == null) + { + drawingData = data; + return; + } + + // Resize the array + byte[] newArray = new byte[drawingData.length + data.length]; + System.arraycopy(drawingData, 0, newArray, 0, drawingData.length); + System.arraycopy(data, 0, newArray, drawingData.length, data.length); + drawingData = newArray; + + // Dirty up this object + initialized = false; + } + + /** + * Accessor for the number of drawings + * + * @return the current count of drawings + */ + final int getNumDrawings() + { + return numDrawings; + } + + /** + * Gets the sp container for the specified drawing number + * + * @param drawingNum the drawing number for which to return the spContainer + * @return the spcontainer + */ + EscherContainer getSpContainer(int drawingNum) + { + if (!initialized) + { + initialize(); + } + + if ( (drawingNum + 1) >= spContainers.length) + { + throw new DrawingDataException(); + } + + EscherContainer spContainer = + (EscherContainer) spContainers[drawingNum + 1]; + + Assert.verify(spContainer != null); + + return spContainer; + } + + /** + * Gets the data which was read in for the drawings + * + * @return the drawing data + */ + public byte[] getData() + { + return drawingData; + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/DrawingDataException.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/DrawingDataException.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/DrawingDataException.java 17 Aug 2012 14:51:20 -0000 1.1 @@ -0,0 +1,37 @@ +/********************************************************************* +* +* Copyright (C) 2006 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +/** + * Checked exception thrown when the drawing data is corrupt eg. when + * the drawing number exceeds the number of SpContainers. This exception + * is handled within the drawing package, and usually causes drawings to be + * disabled for the remainder of the workbook + */ +public class DrawingDataException extends RuntimeException +{ + private static String message = + "Drawing number exceeds available SpContainers"; + + DrawingDataException() + { + super(message); + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/DrawingGroup.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/DrawingGroup.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/DrawingGroup.java 17 Aug 2012 14:51:20 -0000 1.1 @@ -0,0 +1,584 @@ +/********************************************************************* +* +* Copyright (C) 2004 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; + +import common.Assert; +import common.Logger; + +import jxl.read.biff.Record; +import jxl.write.biff.File; + +/** + * This class contains the Excel picture data in Escher format for the + * entire workbook + */ +public class DrawingGroup implements EscherStream +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(DrawingGroup.class); + + /** + * The escher data read in from file + */ + private byte[] drawingData; + + /** + * The top level escher container + */ + private EscherContainer escherData; + + /** + * The Bstore container, which contains all the drawing data + */ + private BStoreContainer bstoreContainer; + + /** + * The initialized flag + */ + private boolean initialized; + + /** + * The list of user added drawings + */ + private ArrayList drawings; + + /** + * The number of blips + */ + private int numBlips; + + /** + * The number of charts + */ + private int numCharts; + + /** + * The number of shape ids used on the second Dgg cluster + */ + private int drawingGroupId; + + /** + * Flag which indicates that at least one of the drawings has been omitted + * from the worksheet + */ + private boolean drawingsOmitted; + + /** + * The origin of this drawing group + */ + private Origin origin; + + /** + * A hash map of images keyed on the file path, containing the + * reference count + */ + private HashMap imageFiles; + + /** + * A count of the next available object id + */ + private int maxObjectId; + + /** + * The maximum shape id so far encountered + */ + private int maxShapeId; + + /** + * Constructor + * + * @param o the origin of this drawing group + */ + public DrawingGroup(Origin o) + { + origin = o; + initialized = o == Origin.WRITE ? true : false; + drawings = new ArrayList(); + imageFiles = new HashMap(); + drawingsOmitted = false; + maxObjectId = 1; + maxShapeId = 1024; + } + + /** + * Copy constructor + * Uses a shallow copy for most things, since as soon as anything + * is changed, the drawing group is invalidated and all the data blocks + * regenerated + * + * @param dg the drawing group to copy + */ + public DrawingGroup(DrawingGroup dg) + { + drawingData = dg.drawingData; + escherData = dg.escherData; + bstoreContainer = dg.bstoreContainer; + initialized = dg.initialized; + drawingData = dg.drawingData; + escherData = dg.escherData; + bstoreContainer = dg.bstoreContainer; + numBlips = dg.numBlips; + numCharts = dg.numCharts; + drawingGroupId = dg.drawingGroupId; + drawingsOmitted = dg.drawingsOmitted; + origin = dg.origin; + imageFiles = (HashMap) dg.imageFiles.clone(); + maxObjectId = dg.maxObjectId; + maxShapeId = dg.maxShapeId; + + // Create this as empty, because all drawings will get added later + // as part of the sheet copy process + drawings = new ArrayList(); + } + + /** + + /** + * Adds in a drawing group record to this drawing group. The binary + * data is extracted from the drawing group and added to a single + * byte array + * + * @param mso the drawing group record to add + */ + public void add(MsoDrawingGroupRecord mso) + { + addData(mso.getData()); + } + + /** + * Adds a continue record to this drawing group. the binary data is + * extracted and appended to the byte array + * + * @param cont the continue record + */ + public void add(Record cont) + { + addData(cont.getData()); + } + + /** + * Adds the mso record data to the drawing data + * + * @param msodata the raw mso data + */ + private void addData(byte[] msodata) + { + if (drawingData == null) + { + drawingData = new byte[msodata.length]; + System.arraycopy(msodata, 0, drawingData, 0, msodata.length); + return; + } + + // Grow the array + byte[] newdata = new byte[drawingData.length + msodata.length]; + System.arraycopy(drawingData, 0, newdata, 0, drawingData.length); + System.arraycopy(msodata, 0, newdata, drawingData.length, msodata.length); + drawingData = newdata; + } + + /** + * Adds a drawing to the drawing group + * + * @param d the drawing to add + */ + final void addDrawing(DrawingGroupObject d) + { + drawings.add(d); + maxObjectId = Math.max(maxObjectId, d.getObjectId()); + maxShapeId = Math.max(maxShapeId, d.getShapeId()); + } + + /** + * Adds a chart to the drawing group + * + * @param c the chart + */ + public void add(Chart c) + { + numCharts++; + } + + /** + * Adds a drawing from the public, writable interface + * + * @param d the drawing to add + */ + public void add(DrawingGroupObject d) + { + if (origin == Origin.READ) + { + origin = Origin.READ_WRITE; + BStoreContainer bsc = getBStoreContainer(); // force initialization + Dgg dgg = (Dgg) escherData.getChildren()[0]; + drawingGroupId = dgg.getCluster(1).drawingGroupId - numBlips - 1; + numBlips = bsc != null ? bsc.getNumBlips() : 0; + + if (bsc != null) + { + Assert.verify(numBlips == bsc.getNumBlips()); + } + } + + if (!(d instanceof Drawing)) + { + // Assign a new object id and add it to the list + // drawings.add(d); + maxObjectId++; + maxShapeId++; + d.setDrawingGroup(this); + d.setObjectId(maxObjectId, numBlips + 1, maxShapeId); + if (drawings.size() > maxObjectId) + { + logger.warn("drawings length " + drawings.size() + + " exceeds the max object id " + maxObjectId); + } + // numBlips++; + return; + } + + Drawing drawing = (Drawing) d; + + // See if this is referenced elsewhere + Drawing refImage = + (Drawing) imageFiles.get(d.getImageFilePath()); + + if (refImage == null) + { + // There are no other references to this drawing, so assign + // a new object id and put it on the hash map + maxObjectId++; + maxShapeId++; + drawings.add(drawing); + drawing.setDrawingGroup(this); + drawing.setObjectId(maxObjectId, numBlips + 1, maxShapeId); + numBlips++; + imageFiles.put(drawing.getImageFilePath(), drawing); + } + else + { + // This drawing is used elsewhere in the workbook. Increment the + // reference count on the drawing, and set the object id of the drawing + // passed in + refImage.setReferenceCount(refImage.getReferenceCount() + 1); + drawing.setDrawingGroup(this); + drawing.setObjectId(refImage.getObjectId(), + refImage.getBlipId(), + refImage.getShapeId()); + } + } + + /** + * Interface method to remove a drawing from the group + * + * @param d the drawing to remove + */ + public void remove(DrawingGroupObject d) + { + // Unless there are real images or some such, it is possible that + // a BStoreContainer will not be present. In that case simply return + if (getBStoreContainer() == null) + { + return; + } + + if (origin == Origin.READ) + { + origin = Origin.READ_WRITE; + numBlips = getBStoreContainer().getNumBlips(); + Dgg dgg = (Dgg) escherData.getChildren()[0]; + drawingGroupId = dgg.getCluster(1).drawingGroupId - numBlips - 1; + } + + // Get the blip + EscherRecord[] children = getBStoreContainer().getChildren(); + BlipStoreEntry bse = (BlipStoreEntry) children[d.getBlipId() - 1]; + + bse.dereference(); + + if (bse.getReferenceCount() == 0) + { + // Remove the blip + getBStoreContainer().remove(bse); + + // Adjust blipId on the other blips + for (Iterator i = drawings.iterator(); i.hasNext();) + { + DrawingGroupObject drawing = (DrawingGroupObject) i.next(); + + if (drawing.getBlipId() > d.getBlipId()) + { + drawing.setObjectId(drawing.getObjectId(), + drawing.getBlipId() - 1, + drawing.getShapeId()); + } + } + + numBlips--; + } + } + + + /** + * Initializes the drawing data from the escher record read in + */ + private void initialize() + { + EscherRecordData er = new EscherRecordData(this, 0); + + Assert.verify(er.isContainer()); + + escherData = new EscherContainer(er); + + Assert.verify(escherData.getLength() == drawingData.length); + Assert.verify(escherData.getType() == EscherRecordType.DGG_CONTAINER); + + initialized = true; + } + + /** + * Gets hold of the BStore container from the Escher data + * + * @return the BStore container + */ + private BStoreContainer getBStoreContainer() + { + if (bstoreContainer == null) + { + if (!initialized) + { + initialize(); + } + + EscherRecord[] children = escherData.getChildren(); + if (children.length > 1 && + children[1].getType() == EscherRecordType.BSTORE_CONTAINER) + { + bstoreContainer = (BStoreContainer) children[1]; + } + } + + return bstoreContainer; + } + + /** + * Gets hold of the binary data + * + * @return the data + */ + public byte[] getData() + { + return drawingData; + } + + /** + * Writes the drawing group to the output file + * + * @param outputFile the file to write to + * @exception IOException + */ + public void write(File outputFile) throws IOException + { + if (origin == Origin.WRITE) + { + DggContainer dggContainer = new DggContainer(); + + Dgg dgg = new Dgg(numBlips + numCharts + 1, numBlips); + + dgg.addCluster(1, 0); + dgg.addCluster(numBlips + 1, 0); + + dggContainer.add(dgg); + + int drawingsAdded = 0; + BStoreContainer bstoreCont = new BStoreContainer(); + + // Create a blip entry for each drawing + for (Iterator i = drawings.iterator(); i.hasNext();) + { + Object o = i.next(); + if (o instanceof Drawing) + { + Drawing d = (Drawing) o; + BlipStoreEntry bse = new BlipStoreEntry(d); + + bstoreCont.add(bse); + drawingsAdded++; + } + } + if (drawingsAdded > 0) + { + bstoreCont.setNumBlips(drawingsAdded); + dggContainer.add(bstoreCont); + } + + Opt opt = new Opt(); + + dggContainer.add(opt); + + SplitMenuColors splitMenuColors = new SplitMenuColors(); + dggContainer.add(splitMenuColors); + + drawingData = dggContainer.getData(); + } + else if (origin == Origin.READ_WRITE) + { + DggContainer dggContainer = new DggContainer(); + + Dgg dgg = new Dgg(numBlips + numCharts + 1, numBlips); + + dgg.addCluster(1, 0); + dgg.addCluster(drawingGroupId + numBlips + 1, 0); + + dggContainer.add(dgg); + + BStoreContainer bstoreCont = new BStoreContainer(); + bstoreCont.setNumBlips(numBlips); + + // Create a blip entry for each drawing that was read in + BStoreContainer readBStoreContainer = getBStoreContainer(); + + if (readBStoreContainer != null) + { + EscherRecord[] children = readBStoreContainer.getChildren(); + for (int i = 0; i < children.length; i++) + { + BlipStoreEntry bse = (BlipStoreEntry) children[i]; + bstoreCont.add(bse); + } + } + + // Create a blip entry for each drawing that has been added + for (Iterator i = drawings.iterator(); i.hasNext();) + { + DrawingGroupObject dgo = (DrawingGroupObject) i.next(); + if (dgo instanceof Drawing) + { + Drawing d = (Drawing) dgo; + if (d.getOrigin() == Origin.WRITE) + { + BlipStoreEntry bse = new BlipStoreEntry(d); + bstoreCont.add(bse); + } + } + } + + dggContainer.add(bstoreCont); + + Opt opt = new Opt(); + + opt.addProperty(191, false, false, 524296); + opt.addProperty(385, false, false, 134217737); + opt.addProperty(448, false, false, 134217792); + + dggContainer.add(opt); + + SplitMenuColors splitMenuColors = new SplitMenuColors(); + dggContainer.add(splitMenuColors); + + drawingData = dggContainer.getData(); + } + + MsoDrawingGroupRecord msodg = new MsoDrawingGroupRecord(drawingData); + outputFile.write(msodg); + } + + /** + * Accessor for the number of blips in the drawing group + * + * @return the number of blips + */ + final int getNumberOfBlips() + { + return numBlips; + } + + /** + * Gets the drawing data for the given blip id. Called by the Drawing + * object + * + * @param blipId the blipId + * @return the drawing data + */ + byte[] getImageData(int blipId) + { + numBlips = getBStoreContainer().getNumBlips(); + + Assert.verify(blipId <= numBlips); + Assert.verify(origin == Origin.READ || origin == Origin.READ_WRITE); + + // Get the blip + EscherRecord[] children = getBStoreContainer().getChildren(); + BlipStoreEntry bse = (BlipStoreEntry) children[blipId - 1]; + + return bse.getImageData(); + } + + /** + * Indicates that at least one of the drawings has been omitted from + * the worksheet + + * @param mso the mso record + * @param obj the obj record + */ + public void setDrawingsOmitted(MsoDrawingRecord mso, ObjRecord obj) + { + drawingsOmitted = true; + + if (obj != null) + { + maxObjectId = Math.max(maxObjectId, obj.getObjectId()); + } + } + + /** + * Accessor for the drawingsOmitted flag + * + * @return TRUE if a drawing has been omitted, FALSE otherwise + */ + public boolean hasDrawingsOmitted() + { + return drawingsOmitted; + } + + /** + * Updates this with the appropriate data from the drawing group passed in + * This is called during the copy process: this is first initialised as + * an empty object, but during the copy, the source DrawingGroup may + * change. After the copy process, this method is then called to update + * the relevant fields. Unfortunately, the copy process required the + * presence of a drawing group + * + * @param dg the drawing group containing the updated data + */ + public void updateData(DrawingGroup dg) + { + drawingsOmitted = dg.drawingsOmitted; + maxObjectId = dg.maxObjectId; + maxShapeId = dg.maxShapeId; + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/DrawingGroupObject.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/DrawingGroupObject.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/DrawingGroupObject.java 17 Aug 2012 14:51:19 -0000 1.1 @@ -0,0 +1,233 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +import java.io.IOException; + +import jxl.write.biff.File; + +/** + * Interface for the various object types that can be added to a drawing + * group + */ +public interface DrawingGroupObject +{ + /** + * Sets the object id. Invoked by the drawing group when the object is + * added to id + * + * @param objid the object id + * @param bip the blip id + * @param sid the shape id + */ + void setObjectId(int objid, int bip, int sid); + + /** + * Accessor for the object id + * + * @return the object id + */ + int getObjectId(); + + /** + * Accessor for the blip id + * + * @return the blip id + */ + int getBlipId(); + + /** + * Accessor for the shape id + * + * @return the shape id + */ + public int getShapeId(); + + + /** + * Gets the drawing record which was read in + * + * @return the drawing record + */ + MsoDrawingRecord getMsoDrawingRecord(); + + /** + * Creates the main Sp container for the drawing + * + * @return the SP container + */ + public EscherContainer getSpContainer(); + + /** + * Sets the drawing group for this drawing. Called by the drawing group + * when this drawing is added to it + * + * @param dg the drawing group + */ + void setDrawingGroup(DrawingGroup dg); + + /** + * Accessor for the drawing group + * + * @return the drawing group + */ + DrawingGroup getDrawingGroup(); + + /** + * Gets the origin of this drawing + * + * @return where this drawing came from + */ + Origin getOrigin(); + + /** + * Accessor for the reference count on this drawing + * + * @return the reference count + */ + int getReferenceCount(); + + /** + * Sets the new reference count on the drawing + * + * @param r the new reference count + */ + void setReferenceCount(int r); + + /** + * Accessor for the column of this drawing + * + * @return the column + */ + public double getX(); + + /** + * Sets the column position of this drawing + * + * @param x the column + */ + public void setX(double x); + + /** + * Accessor for the row of this drawing + * + * @return the row + */ + public double getY(); + + /** + * Accessor for the row of the drawing + * + * @param y the row + */ + public void setY(double y); + + /** + * Accessor for the width of this drawing + * + * @return the number of columns spanned by this image + */ + public double getWidth(); + + /** + * Accessor for the width + * + * @param w the number of columns to span + */ + public void setWidth(double w); + + /** + * Accessor for the height of this drawing + * + * @return the number of rows spanned by this image + */ + public double getHeight(); + + /** + * Accessor for the height of this drawing + * + * @param h the number of rows spanned by this image + */ + public void setHeight(double h); + + + /** + * Accessor for the type + * + * @return the type + */ + ShapeType getType(); + + /** + * Accessor for the image data + * + * @return the image data + */ + public byte[] getImageData(); + + /** + * Accessor for the image data + * + * @return the image data + */ + public byte[] getImageBytes() throws IOException; + + /** + * Accessor for the image file path. Normally this is the absolute path + * of a file on the directory system, but if this drawing was constructed + * using an byte[] then the blip id is returned + * + * @return the image file path, or the blip id + */ + String getImageFilePath(); + + /** + * Writes any other records associated with this drawing group object + */ + public void writeAdditionalRecords(File outputFile) throws IOException; + + /** + * Writes any records that need to be written after all the drawing group + * objects have been written + */ + public void writeTailRecords(File outputFile) throws IOException; + + /** + * Accessor for the first drawing on the sheet. This is used when + * copying unmodified sheets to indicate that this drawing contains + * the first time Escher gubbins + * + * @return TRUE if this MSORecord is the first drawing on the sheet + */ + public boolean isFirst(); + + /** + * Queries whether this object is a form object. Form objects have their + * drawings records spread over TXO and CONTINUE records and + * require special handling + * + * @return TRUE if this is a form object, FALSE otherwise + */ + public boolean isFormObject(); + +} + + + Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/EscherAtom.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/EscherAtom.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/EscherAtom.java 17 Aug 2012 14:51:19 -0000 1.1 @@ -0,0 +1,67 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +import common.Logger; + +/** + * Class for atoms. This may be instantiated as is for unknown/uncared about + * atoms, or subclassed if we have some semantic interest in the contents + */ +class EscherAtom extends EscherRecord +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(EscherAtom.class); + + /** + * Constructor + * + * @param erd the escher record data + */ + public EscherAtom(EscherRecordData erd) + { + super(erd); + } + + /** + * Constructor + * + * @param type the type + */ + protected EscherAtom(EscherRecordType type) + { + super(type); + } + + /** + * Gets the data for writing + * + * @return the data + */ + byte[] getData() + { + logger.warn("escher atom getData called on object of type " + + getClass().getName() + " code " + + Integer.toString(getType().getValue(), 16)); + return null; + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/EscherContainer.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/EscherContainer.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/EscherContainer.java 17 Aug 2012 14:51:19 -0000 1.1 @@ -0,0 +1,218 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +import java.util.ArrayList; +import java.util.Iterator; + +import common.Logger; + +/** + * An escher container. This record may contain other escher containers or + * atoms + */ +class EscherContainer extends EscherRecord +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(EscherContainer.class); + + /** + * Initialized flag + */ + private boolean initialized; + + + /** + * The children of this container + */ + private ArrayList children; + + /** + * Constructor + * + * @param erd the raw data + */ + public EscherContainer(EscherRecordData erd) + { + super(erd); + initialized = false; + children = new ArrayList(); + } + + /** + * Constructor used when writing out escher data + * + * @param type the type + */ + protected EscherContainer(EscherRecordType type) + { + super(type); + setContainer(true); + children = new ArrayList(); + } + + /** + * Accessor for the children of this container + * + * @return the children + */ + public EscherRecord[] getChildren() + { + if (!initialized) + { + initialize(); + } + + Object[] ca = children.toArray(new EscherRecord[children.size()]); + + return (EscherRecord[]) ca; + } + + /** + * Adds a child to this container + * + * @param child the item to add + */ + public void add(EscherRecord child) + { + children.add(child); + } + + /** + * Removes a child from this container + * + * @param child the item to remove + */ + public void remove(EscherRecord child) + { + boolean result = children.remove(child); + } + + /** + * Initialization + */ + private void initialize() + { + int curpos = getPos() + HEADER_LENGTH; + int endpos = Math.min(getPos() + getLength(), getStreamLength()); + + EscherRecord newRecord = null; + + while (curpos < endpos) + { + EscherRecordData erd = new EscherRecordData(getEscherStream(), curpos); + + EscherRecordType type = erd.getType(); + if (type == EscherRecordType.DGG) + { + newRecord = new Dgg(erd); + } + else if (type == EscherRecordType.DG) + { + newRecord = new Dg(erd); + } + else if (type == EscherRecordType.BSTORE_CONTAINER) + { + newRecord = new BStoreContainer(erd); + } + else if (type == EscherRecordType.SPGR_CONTAINER) + { + newRecord = new SpgrContainer(erd); + } + else if (type == EscherRecordType.SP_CONTAINER) + { + newRecord = new SpContainer(erd); + } + else if (type == EscherRecordType.SPGR) + { + newRecord = new Spgr(erd); + } + else if (type == EscherRecordType.SP) + { + newRecord = new Sp(erd); + } + else if (type == EscherRecordType.CLIENT_ANCHOR) + { + newRecord = new ClientAnchor(erd); + } + else if (type == EscherRecordType.CLIENT_DATA) + { + newRecord = new ClientData(erd); + } + else if (type == EscherRecordType.BSE) + { + newRecord = new BlipStoreEntry(erd); + } + else if (type == EscherRecordType.OPT) + { + newRecord = new Opt(erd); + } + else if (type == EscherRecordType.SPLIT_MENU_COLORS) + { + newRecord = new SplitMenuColors(erd); + } + else if (type == EscherRecordType.CLIENT_TEXT_BOX) + { + newRecord = new ClientTextBox(erd); + } + else + { + newRecord = new EscherAtom(erd); + } + + children.add(newRecord); + curpos += newRecord.getLength(); + } + + initialized = true; + } + + /** + * Gets the data for this container (and all of its children recursively + * + * @return the binary data + */ + byte[] getData() + { + if (!initialized) + { + initialize(); + } + + byte[] data = new byte[0]; + for (Iterator i = children.iterator(); i.hasNext();) + { + EscherRecord er = (EscherRecord) i.next(); + byte[] childData = er.getData(); + + if (childData != null) + { + byte[] newData = new byte[data.length + childData.length]; + System.arraycopy(data, 0, newData, 0, data.length); + System.arraycopy(childData, 0, newData, data.length, childData.length); + data = newData; + } + } + + return setHeaderData(data); + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/EscherDisplay.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/EscherDisplay.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/EscherDisplay.java 17 Aug 2012 14:51:19 -0000 1.1 @@ -0,0 +1,212 @@ +/********************************************************************* +* +* Copyright (C) 2005 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +import java.io.BufferedWriter; +import java.io.IOException; + +/** + * Class used to display a complete hierarchically organized Escher stream + * The whole thing is dumped to System.out + * + * This class is only used as a debugging tool + */ +public class EscherDisplay +{ + /** + * The escher stream + */ + private EscherStream stream; + + /** + * The writer + */ + private BufferedWriter writer; + + /** + * Constructor + * + * @param s the stream + * @param bw the writer + */ + public EscherDisplay(EscherStream s, BufferedWriter bw) + { + stream = s; + writer = bw; + } + + /** + * Display the formatted escher stream + * + * @exception IOException + */ + public void display() throws IOException + { + EscherRecordData er = new EscherRecordData(stream, 0); + EscherContainer ec = new EscherContainer(er); + displayContainer(ec, 0); + } + + /** + * Displays the escher container as text + * + * @param ec the escher container + * @param level the indent level + * @exception IOException + */ + private void displayContainer(EscherContainer ec, int level) + throws IOException + { + displayRecord(ec, level); + + // Display the contents of the container + level++; + + EscherRecord[] children = ec.getChildren(); + + for (int i = 0; i < children.length; i++) + { + EscherRecord er = children[i]; + if (er.getEscherData().isContainer()) + { + displayContainer((EscherContainer) er, level); + } + else + { + displayRecord(er, level); + } + } + } + + /** + * Displays an escher record + * + * @param er the record to display + * @param level the amount of indentation + * @exception IOException + */ + private void displayRecord(EscherRecord er, int level) + throws IOException + { + indent(level); + + EscherRecordType type = er.getType(); + + // Display the code + writer.write(Integer.toString(type.getValue(), 16)); + writer.write(" - "); + + // Display the name + if (type == EscherRecordType.DGG_CONTAINER) + { + writer.write("Dgg Container"); + writer.newLine(); + } + else if (type == EscherRecordType.BSTORE_CONTAINER) + { + writer.write("BStore Container"); + writer.newLine(); + } + else if (type == EscherRecordType.DG_CONTAINER) + { + writer.write("Dg Container"); + writer.newLine(); + } + else if (type == EscherRecordType.SPGR_CONTAINER) + { + writer.write("Spgr Container"); + writer.newLine(); + } + else if (type == EscherRecordType.SP_CONTAINER) + { + writer.write("Sp Container"); + writer.newLine(); + } + else if (type == EscherRecordType.DGG) + { + writer.write("Dgg"); + writer.newLine(); + } + else if (type == EscherRecordType.BSE) + { + writer.write("Bse"); + writer.newLine(); + } + else if (type == EscherRecordType.DG) + { + writer.write("Dg"); + writer.newLine(); + } + else if (type == EscherRecordType.SPGR) + { + writer.write("Spgr"); + writer.newLine(); + } + else if (type == EscherRecordType.SP) + { + writer.write("Sp"); + writer.newLine(); + } + else if (type == EscherRecordType.OPT) + { + writer.write("Opt"); + writer.newLine(); + } + else if (type == EscherRecordType.CLIENT_ANCHOR) + { + writer.write("Client Anchor"); + writer.newLine(); + } + else if (type == EscherRecordType.CLIENT_DATA) + { + writer.write("Client Data"); + writer.newLine(); + } + else if (type == EscherRecordType.CLIENT_TEXT_BOX) + { + writer.write("Client Text Box"); + writer.newLine(); + } + else if (type == EscherRecordType.SPLIT_MENU_COLORS) + { + writer.write("Split Menu Colors"); + writer.newLine(); + } + else + { + writer.write("???"); + writer.newLine(); + } + } + + /** + * Indents to the amount specified by the level + * + * @param level the level + * @exception IOException + */ + private void indent(int level) throws IOException + { + for (int i = 0; i < level * 2; i++) + { + writer.write(' '); + } + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/EscherRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/EscherRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/EscherRecord.java 17 Aug 2012 14:51:20 -0000 1.1 @@ -0,0 +1,196 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +import common.Logger; + +/** + * The base class for all escher records. This class contains + * the common header data and is basically a wrapper for the EscherRecordData + * object + */ +abstract class EscherRecord +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(EscherRecord.class); + + /** + * The escher data + */ + private EscherRecordData data; + //protected EscherRecordData data; + + /** + * The length of the escher header on all records + */ + protected static final int HEADER_LENGTH = 8; + + /** + * Constructor + * + * @param erd the data + */ + protected EscherRecord(EscherRecordData erd) + { + data = erd; + } + + /** + * Constructor + * + * @param type the type + */ + protected EscherRecord(EscherRecordType type) + { + data = new EscherRecordData(type); + } + + /** + * Identifies whether this item is a container + * + * @param cont TRUE if this is a container, FALSE otherwise + */ + protected void setContainer(boolean cont) + { + data.setContainer(cont); + } + + /** + * Gets the entire length of the record, including the header + * + * @return the length of the record, including the header data + */ + public int getLength() + { + return data.getLength() + HEADER_LENGTH; + } + + /** + * Accessor for the escher stream + * + * @return the escher stream + */ + protected final EscherStream getEscherStream() + { + return data.getEscherStream(); + } + + /** + * The position of this escher record in the stream + * + * @return the position + */ + protected final int getPos() + { + return data.getPos(); + } + + /** + * Accessor for the instance + * + * @return the instance + */ + protected final int getInstance() + { + return data.getInstance(); + } + + /** + * Sets the instance number when writing out the escher data + * + * @param i the instance + */ + protected final void setInstance(int i) + { + data.setInstance(i); + } + + /** + * Sets the version when writing out the escher data + * + * @param v the version + */ + protected final void setVersion (int v) + { + data.setVersion(v); + } + + /** + * Accessor for the escher type + * + * @return the type + */ + public EscherRecordType getType() + { + return data.getType(); + } + + /** + * Abstract method used to retrieve the generated escher data when writing + * out image information + * + * @return the escher data + */ + abstract byte[] getData(); + + /** + * Prepends the standard header data to the first eight bytes of the array + * and returns it + * + * @param d the data + * @return the binary data + */ + final byte[] setHeaderData(byte[] d) + { + return data.setHeaderData(d); + } + + /** + * Gets the data that was read in, excluding the header data + * + * @return the bytes read in, excluding the header data + */ + byte[] getBytes() + { + return data.getBytes(); + } + + /** + * Accessor for the stream length + * + * @return the stream length + */ + protected int getStreamLength() + { + return data.getStreamLength(); + } + + /** + * Used by the EscherDisplay class to retrieve the data + * + * @return the data + */ + protected EscherRecordData getEscherData() + { + return data; + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/EscherRecordData.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/EscherRecordData.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/EscherRecordData.java 17 Aug 2012 14:51:20 -0000 1.1 @@ -0,0 +1,313 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +import common.Logger; + +import jxl.biff.IntegerHelper; + + +/** + * A single record from an Escher stream. Basically this a container for + * the header data for each Escher record + */ +final class EscherRecordData +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(EscherRecordData.class); + + /** + * The byte position of this record in the escher stream + */ + private int pos; + + /** + * The instance value + */ + private int instance; + + /** + * The version value + */ + private int version; + + /** + * The record id + */ + private int recordId; + + /** + * The length of the record, excluding the 8 byte header + */ + private int length; + + /** + * The length of the stream + */ + private int streamLength; + + /** + * Indicates whether this record is a container + */ + private boolean container; + + /** + * The type of this record + */ + private EscherRecordType type; + + /** + * A handle back to the drawing group, which contains the entire escher + * stream byte data + */ + private EscherStream escherStream; + + /** + * Constructor + * + * @param dg the escher stream data + * @param p the current position in the stream + */ + public EscherRecordData(EscherStream dg, int p) + { + escherStream = dg; + pos = p; + byte[] data = escherStream.getData(); + + streamLength = data.length; + + // First two bytes contain instance and version + int value = IntegerHelper.getInt(data[pos], data[pos + 1]); + + // Instance value is the first 12 bits + instance = (value & 0xfff0) >> 4; + + // Version is the last four bits + version = value & 0xf; + + // Bytes 2 and 3 are the record id + recordId = IntegerHelper.getInt(data[pos + 2], data[pos + 3]); + + // Length is bytes 4,5,6 and 7 + length = IntegerHelper.getInt(data[pos + 4], data[pos + 5], + data[pos + 6], data[pos + 7]); + + if (version == 0x0f) + { + container = true; + } + else + { + container = false; + } + } + + /** + * Constructor + * + * @param t the type of the escher record + */ + public EscherRecordData(EscherRecordType t) + { + type = t; + recordId = type.getValue(); + } + + /** + * Determines whether this record is a container + * + * @return TRUE if this is a container, FALSE otherwise + */ + public boolean isContainer() + { + return container; + } + + /** + * Accessor for the length, excluding the 8 byte header + * + * @return the length excluding the 8 byte header + */ + public int getLength() + { + return length; + } + + /** + * Accessor for the record id + * + * @return the record id + */ + public int getRecordId() + { + return recordId; + } + + /** + * Accessor for the drawing group stream + * + * @return the drawing group stream + */ + EscherStream getDrawingGroup() + { + return escherStream; + } + + /** + * Gets the position in the stream + * + * @return the position in the stream + */ + int getPos() + { + return pos; + } + + /** + * Gets the escher type of this record + * + * @return the escher type + */ + EscherRecordType getType() + { + if (type == null) + { + type = EscherRecordType.getType(recordId); + } + + return type; + } + + /** + * Gets the instance value + * + * @return the instance value + */ + int getInstance() + { + return instance; + } + + /** + * Sets whether or not this is a container - called when writing + * out an escher stream + * + * @param c TRUE if this is a container, FALSE otherwise + */ + void setContainer(boolean c) + { + container = c; + } + + /** + * Called from the subclass when writing to set the instance value + * + * @param inst the instance + */ + void setInstance(int inst) + { + instance = inst; + } + + /** + * Called when writing to set the length of this record + * + * @param l the length + */ + void setLength(int l) + { + length = l; + } + + /** + * Called when writing to set the version of this record + * + * @param v the version + */ + void setVersion(int v) + { + version = v; + } + + /** + * Adds the 8 byte header data on the value data passed in, returning + * the modified data + * + * @param d the value data + * @return the value data with the header information + */ + byte[] setHeaderData(byte[] d) + { + byte[] data = new byte[d.length + 8]; + System.arraycopy(d, 0, data, 8, d.length); + + if (container) + { + version = 0x0f; + } + + // First two bytes contain instance and version + int value = instance << 4; + value |= version; + IntegerHelper.getTwoBytes(value, data, 0); + + // Bytes 2 and 3 are the record id + IntegerHelper.getTwoBytes(recordId, data, 2); + + // Length is bytes 4,5,6 and 7 + IntegerHelper.getFourBytes(d.length, data, 4); + + return data; + } + + /** + * Accessor for the header stream + * + * @return the escher stream + */ + EscherStream getEscherStream() + { + return escherStream; + } + + /** + * Gets the data that was read in, excluding the header data + * + * @return the value data that was read in + */ + byte[] getBytes() + { + byte[] d = new byte[length]; + System.arraycopy(escherStream.getData(), pos + 8, d, 0, length); + return d; + } + + /** + * Accessor for the stream length + * + * @return the stream length + */ + int getStreamLength() + { + return streamLength; + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/EscherRecordType.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/EscherRecordType.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/EscherRecordType.java 17 Aug 2012 14:51:19 -0000 1.1 @@ -0,0 +1,111 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +/** + * Enumeration class for Escher record types + */ +final class EscherRecordType +{ + /** + * The code of the item within the escher stream + */ + private int value; + + /** + * All escher types + */ + private static EscherRecordType[] types = new EscherRecordType[0]; + + /** + * Constructor + * + * @param val the escher record value + */ + private EscherRecordType(int val) + { + value = val; + + EscherRecordType[] newtypes = new EscherRecordType[types.length + 1]; + System.arraycopy(types, 0, newtypes, 0, types.length); + newtypes[types.length] = this; + types = newtypes; + } + + /** + * Accessor for the escher record value + * + * @return the escher record value + */ + public int getValue() + { + return value; + } + + /** + * Accessor to get the item from a particular value + * + * @param val the escher record value + * @return the type corresponding to val, or UNKNOWN if a match could not + * be found + */ + public static EscherRecordType getType(int val) + { + EscherRecordType type = UNKNOWN; + + for (int i = 0; i < types.length; i++) + { + if (val == types[i].value) + { + type = types[i]; + break; + } + } + + return type; + } + + public static final EscherRecordType UNKNOWN = new EscherRecordType(0x0); + public static final EscherRecordType DGG_CONTAINER = + new EscherRecordType(0xf000); + public static final EscherRecordType BSTORE_CONTAINER = + new EscherRecordType(0xf001); + public static final EscherRecordType DG_CONTAINER = + new EscherRecordType(0xf002); + public static final EscherRecordType SPGR_CONTAINER = + new EscherRecordType(0xf003); + public static final EscherRecordType SP_CONTAINER = + new EscherRecordType(0xf004); + + public static final EscherRecordType DGG = new EscherRecordType(0xf006); + public static final EscherRecordType BSE = new EscherRecordType(0xf007); + public static final EscherRecordType DG = new EscherRecordType(0xf008); + public static final EscherRecordType SPGR = new EscherRecordType(0xf009); + public static final EscherRecordType SP = new EscherRecordType(0xf00a); + public static final EscherRecordType OPT = new EscherRecordType(0xf00b); + public static final EscherRecordType CLIENT_ANCHOR = + new EscherRecordType(0xf010); + public static final EscherRecordType CLIENT_DATA = + new EscherRecordType(0xf011); + public static final EscherRecordType CLIENT_TEXT_BOX = + new EscherRecordType(0xf00d); + public static final EscherRecordType SPLIT_MENU_COLORS = + new EscherRecordType(0xf11e); +} Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/EscherStream.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/EscherStream.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/EscherStream.java 17 Aug 2012 14:51:19 -0000 1.1 @@ -0,0 +1,33 @@ +/********************************************************************* +* +* Copyright (C) 2004 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +/** + * Interface implemented by records which contain escher byte streams + */ +interface EscherStream +{ + /** + * Method to access the escher data + * + * @return the escher data + */ + byte[] getData(); +} Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/MsoDrawingGroupRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/MsoDrawingGroupRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/MsoDrawingGroupRecord.java 17 Aug 2012 14:51:19 -0000 1.1 @@ -0,0 +1,72 @@ +/********************************************************************* +* +* Copyright (C) 2001 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; +import jxl.read.biff.Record; + +/** + * A record which merely holds the MSODRAWINGGROUP data. Used when copying + * files which contain images + */ +public class MsoDrawingGroupRecord extends WritableRecordData +{ + /** + * The binary data + */ + private byte[] data; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + */ + public MsoDrawingGroupRecord(Record t) + { + super(t); + data = t.getData(); + } + + /** + * Constructor + * + * @param d the data + */ + MsoDrawingGroupRecord(byte[] d) + { + super(Type.MSODRAWINGGROUP); + data = d; + } + + /** + * Expose the protected function to the SheetImpl in this package + * + * @return the raw record data + */ + public byte[] getData() + { + return data; + } +} + + + + Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/MsoDrawingRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/MsoDrawingRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/MsoDrawingRecord.java 17 Aug 2012 14:51:19 -0000 1.1 @@ -0,0 +1,116 @@ +/********************************************************************* +* +* Copyright (C) 2001 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +import common.Logger; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; +import jxl.read.biff.Record; + +/** + * A record which merely holds the MSODRAWING data. Used when copying files + * which contain images + */ +public class MsoDrawingRecord extends WritableRecordData +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(MsoDrawingRecord.class); + + /** + * Flag to indicate whether this is the first drawing on the sheet + * - needed for copying + */ + private boolean first; + /** + * The raw drawing data which was read in + */ + private byte[] data; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + */ + public MsoDrawingRecord(Record t) + { + super(t); + data = getRecord().getData(); + first = false; + } + + /** + * Constructor + * + * @param d the drawing data + */ + public MsoDrawingRecord(byte[] d) + { + super(Type.MSODRAWING); + data = d; + first = false; + } + + /** + * Expose the protected function + * + * @return the raw record data + */ + public byte[] getData() + { + return data; + } + + /** + * Expose the protected function to the SheetImpl in this package + * + * @return the raw record data + */ + public Record getRecord() + { + return super.getRecord(); + } + + /** + * Sets the flag to indicate that this is the first drawing on the sheet + */ + public void setFirst() + { + first = true; + } + + /** + * Accessor for the first drawing on the sheet. This is used when + * copying unmodified sheets to indicate that this drawing contains + * the first time Escher gubbins + * + * @return TRUE if this MSORecord is the first drawing on the sheet + */ + public boolean isFirst() + { + return first; + } +} + + + + Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/NoteRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/NoteRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/NoteRecord.java 17 Aug 2012 14:51:19 -0000 1.1 @@ -0,0 +1,167 @@ +/********************************************************************* +* +* Copyright (C) 2004 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +import common.Logger; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; +import jxl.read.biff.Record; + +/** + * A Note (TXO) record which contains the information for comments + */ +public class NoteRecord extends WritableRecordData +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(NoteRecord.class); + + /** + * The raw drawing data which was read in + */ + private byte[] data; + + /** + * The row + */ + private int row; + + /** + * The column + */ + private int column; + + /** + * The object id + */ + private int objectId; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + */ + public NoteRecord(Record t) + { + super(t); + data = getRecord().getData(); + row = IntegerHelper.getInt(data[0], data[1]); + column = IntegerHelper.getInt(data[2], data[3]); + objectId = IntegerHelper.getInt(data[6], data[7]); + } + + /** + * Constructor + * + * @param d the drawing data + */ + public NoteRecord(byte[] d) + { + super(Type.NOTE); + data = d; + } + + /** + * Constructor used when writing a Note + * + * @param c the column + * @param r the row + * @param id the object id + */ + public NoteRecord(int c, int r, int id) + { + super(Type.NOTE); + row = r; + column = c; + objectId = id; + } + + /** + * Expose the protected function to the SheetImpl in this package + * + * @return the raw record data + */ + public byte[] getData() + { + if (data != null) + { + return data; + } + + String author = ""; + data = new byte[8 + author.length() + 4]; + + // the row + IntegerHelper.getTwoBytes(row, data, 0); + + // the column + IntegerHelper.getTwoBytes(column, data, 2); + + // the object id + IntegerHelper.getTwoBytes(objectId, data, 6); + + // the length of the string + IntegerHelper.getTwoBytes(author.length(), data, 8); + + // the string + // StringHelper.getBytes(author, data, 11); + + // data[data.length-1]=(byte)0x24; + + return data; + } + + /** + * Accessor for the row + * + * @return the row + */ + int getRow() + { + return row; + } + + /** + * Accessor for the column + * + * @return the column + */ + int getColumn() + { + return column; + } + + /** + * Accessor for the object id + * + * @return the object id + */ + public int getObjectId() + { + return objectId; + } +} + + + + Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/ObjRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/ObjRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/ObjRecord.java 17 Aug 2012 14:51:21 -0000 1.1 @@ -0,0 +1,411 @@ +/********************************************************************* +* +* Copyright (C) 2001 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +import common.Assert; +import common.Logger; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; +import jxl.read.biff.Record; + +/** + * A record which merely holds the OBJ data. Used when copying files which + * contain images + */ +public class ObjRecord extends WritableRecordData +{ + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(ObjRecord.class); + + /** + * The object type + */ + private ObjType type; + + /** + * Indicates whether this record was read in + */ + private boolean read; + + /** + * The object id + */ + private int objectId; + + /** + * Object type enumeration + */ + private static final class ObjType + { + public int value; + public String desc; + + private static ObjType[] types = new ObjType[0]; + + ObjType(int v, String d) + { + value = v; + desc = d; + + ObjType[] oldtypes = types; + types = new ObjType[types.length + 1]; + System.arraycopy(oldtypes, 0, types, 0, oldtypes.length); + types[oldtypes.length] = this; + } + + public String toString() + { + return desc; + } + + public static ObjType getType(int val) + { + ObjType retval = UNKNOWN; + for (int i = 0; i < types.length && retval == UNKNOWN; i++) + { + if (types[i].value == val) + { + retval = types[i]; + } + } + return retval; + } + } + + // The object types + public static final ObjType TBD2 = new ObjType(0x01, "TBD2"); + public static final ObjType TBD = new ObjType(0x02, "TBD"); + public static final ObjType CHART = new ObjType(0x05, "Chart"); + public static final ObjType TEXT = new ObjType(0x06, "Text"); + public static final ObjType BUTTON = new ObjType(0x07, "Button"); + public static final ObjType PICTURE = new ObjType(0x08, "Picture"); + public static final ObjType CHECKBOX = new ObjType(0x0e, "Checkbox"); + public static final ObjType OPTION = new ObjType(0x0c, "Option"); + public static final ObjType EDITBOX = new ObjType(0x0d, "Edit Box"); + public static final ObjType LABEL = new ObjType(0x0e, "Label"); + public static final ObjType DIALOGUEBOX = new ObjType(0x0f, "Dialogue Box"); + public static final ObjType LISTBOX = new ObjType(0x12, "List Box"); + public static final ObjType GROUPBOX = new ObjType(0x13, "Group Box"); + public static final ObjType COMBOBOX = new ObjType(0x14, "Combo Box"); + public static final ObjType MSOFFICEDRAWING = new ObjType + (0x1e, "MS Office Drawing"); + public static final ObjType FORMCONTROL = + new ObjType (0x14, "Form Combo Box"); + public static final ObjType EXCELNOTE = + new ObjType (0x19, "Excel Note"); + + public static final ObjType UNKNOWN = new ObjType(0xff, "Unknown"); + + // Field sub records + private static final int COMMON_DATA_LENGTH = 22; + private static final int CLIPBOARD_FORMAT_LENGTH = 6; + private static final int PICTURE_OPTION_LENGTH = 6; + private static final int NOTE_STRUCTURE_LENGTH = 26; + private static final int COMBOBOX_STRUCTURE_LENGTH = 44; + private static final int END_LENGTH = 4; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + */ + public ObjRecord(Record t) + { + super(t); + byte[] data = t.getData(); + int objtype = IntegerHelper.getInt(data[4], data[5]); + read = true; + type = ObjType.getType(objtype); + + if (type == UNKNOWN) + { + logger.warn("unknown object type code " + objtype); + } + + objectId = IntegerHelper.getInt(data[6], data[7]); + } + + /** + * Constructor + * + * @param objId the object id + * @param t the object type + */ + ObjRecord(int objId, ObjType t) + { + super(Type.OBJ); + objectId = objId; + type = t; + } + + /** + * Expose the protected function to the SheetImpl in this package + * + * @return the raw record data + */ + public byte[] getData() + { + if (read) + { + return getRecord().getData(); + } + + if (type == PICTURE || type == CHART) + { + return getPictureData(); + } + else if (type == EXCELNOTE) + { + return getNoteData(); + } + else if (type == COMBOBOX) + { + return getComboBoxData(); + } + else + { + Assert.verify(false); + } + return null; + } + + /** + * Gets the ObjRecord subrecords for a picture + * + * @return the binary data for the picture + */ + private byte[] getPictureData() + { + int dataLength = COMMON_DATA_LENGTH + + CLIPBOARD_FORMAT_LENGTH + + PICTURE_OPTION_LENGTH + + END_LENGTH; + int pos = 0; + byte[] data = new byte[dataLength]; + + // The common data + // record id + IntegerHelper.getTwoBytes(0x15, data, pos); + + // record length + IntegerHelper.getTwoBytes(COMMON_DATA_LENGTH - 4, data, pos + 2); + + // object type + IntegerHelper.getTwoBytes(type.value, data, pos + 4); + + // object id + IntegerHelper.getTwoBytes(objectId, data, pos + 6); + + // the options + IntegerHelper.getTwoBytes(0x6011, data, pos + 8); + pos += COMMON_DATA_LENGTH; + + // The clipboard format + // record id + IntegerHelper.getTwoBytes(0x7, data, pos); + + // record length + IntegerHelper.getTwoBytes(CLIPBOARD_FORMAT_LENGTH - 4, data, pos + 2); + + // the data + IntegerHelper.getTwoBytes(0xffff, data, pos + 4); + pos += CLIPBOARD_FORMAT_LENGTH; + + // Picture option flags + // record id + IntegerHelper.getTwoBytes(0x8, data, pos); + + // record length + IntegerHelper.getTwoBytes(PICTURE_OPTION_LENGTH - 4, data, pos + 2); + + // the data + IntegerHelper.getTwoBytes(0x1, data, pos + 4); + pos += CLIPBOARD_FORMAT_LENGTH; + + // End record id + IntegerHelper.getTwoBytes(0x0, data, pos); + + // record length + IntegerHelper.getTwoBytes(END_LENGTH - 4, data, pos + 2); + + // the data + pos += END_LENGTH; + + return data; + } + + /** + * Gets the ObjRecord subrecords for a note + * + * @return the note data + */ + private byte[] getNoteData() + { + int dataLength = COMMON_DATA_LENGTH + + NOTE_STRUCTURE_LENGTH + + END_LENGTH; + int pos = 0; + byte[] data = new byte[dataLength]; + + // The common data + // record id + IntegerHelper.getTwoBytes(0x15, data, pos); + + // record length + IntegerHelper.getTwoBytes(COMMON_DATA_LENGTH - 4, data, pos + 2); + + // object type + IntegerHelper.getTwoBytes(type.value, data, pos + 4); + + // object id + IntegerHelper.getTwoBytes(objectId, data, pos + 6); + + // the options + IntegerHelper.getTwoBytes(0x4011, data, pos + 8); + pos += COMMON_DATA_LENGTH; + + // The note structure + // record id + IntegerHelper.getTwoBytes(0xd, data, pos); + + // record length + IntegerHelper.getTwoBytes(NOTE_STRUCTURE_LENGTH - 4, data, pos + 2); + + // the data + pos += NOTE_STRUCTURE_LENGTH; + + // End + // record id + IntegerHelper.getTwoBytes(0x0, data, pos); + + // record length + IntegerHelper.getTwoBytes(END_LENGTH - 4, data, pos + 2); + + // the data + pos += END_LENGTH; + + return data; + } + + /** + * Gets the ObjRecord subrecords for a combo box + * + * @return returns the binary data for a combo box + */ + private byte[] getComboBoxData() + { + int dataLength = COMMON_DATA_LENGTH + + COMBOBOX_STRUCTURE_LENGTH + + END_LENGTH; + int pos = 0; + byte[] data = new byte[dataLength]; + + // The common data + // record id + IntegerHelper.getTwoBytes(0x15, data, pos); + + // record length + IntegerHelper.getTwoBytes(COMMON_DATA_LENGTH - 4, data, pos + 2); + + // object type + IntegerHelper.getTwoBytes(type.value, data, pos + 4); + + // object id + IntegerHelper.getTwoBytes(objectId, data, pos + 6); + + // the options + IntegerHelper.getTwoBytes(0x0, data, pos + 8); + pos += COMMON_DATA_LENGTH; + + // The combo box structure + // record id + IntegerHelper.getTwoBytes(0xc, data, pos); + + // record length + IntegerHelper.getTwoBytes(0x14, data, pos + 2); + + // the data + data[pos + 14] = 0x01; + data[pos + 16] = 0x04; + data[pos + 20] = 0x10; + data[pos + 24] = 0x13; + data[pos + 26] = (byte) 0xee; + data[pos + 27] = 0x1f; + data[pos + 30] = 0x04; + data[pos + 34] = 0x01; + data[pos + 35] = 0x06; + data[pos + 38] = 0x02; + data[pos + 40] = 0x08; + data[pos + 42] = 0x40; + + pos += COMBOBOX_STRUCTURE_LENGTH; + + // End + // record id + IntegerHelper.getTwoBytes(0x0, data, pos); + + // record length + IntegerHelper.getTwoBytes(END_LENGTH - 4, data, pos + 2); + + // the data + pos += END_LENGTH; + + return data; + } + + + /** + * Expose the protected function to the SheetImpl in this package + * + * @return the raw record data + */ + public Record getRecord() + { + return super.getRecord(); + } + + /** + * Accessor for the object type + * + * @return the object type + */ + public ObjType getType() + { + return type; + } + + /** + * Accessor for the object id + * + * @return accessor for the object id + */ + public int getObjectId() + { + return objectId; + } +} + + + + Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/Opt.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/Opt.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/Opt.java 17 Aug 2012 14:51:21 -0000 1.1 @@ -0,0 +1,259 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +import java.util.ArrayList; +import java.util.Iterator; + +import common.Logger; + +import jxl.biff.IntegerHelper; +import jxl.biff.StringHelper; + +/** + * An options record in the escher stream + */ +class Opt extends EscherAtom +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(Opt.class); + + /** + * The binary data + */ + private byte[] data; + + /** + * The number of properties + */ + private int numProperties; + + /** + * The list of properties + */ + private ArrayList properties; + + /** + * Properties enumeration inner class + */ + final static class Property + { + int id; + boolean blipId; + boolean complex; + int value; + String stringValue; + + /** + * Constructor + * + * @param i the property id + * @param bl the blip id + * @param co complex flag + * @param v the value + */ + public Property(int i, boolean bl, boolean co, int v) + { + id = i; + blipId = bl; + complex = co; + value = v; + } + + /** + * Constructor + * + * @param i the property id + * @param bl the blip id + * @param co complex flag + * @param v the value + * @param s the property string + */ + public Property(int i, boolean bl, boolean co, int v, String s) + { + id = i; + blipId = bl; + complex = co; + value = v; + stringValue = s; + } + } + + /** + * Constructor + * + * @param erd the escher record data + */ + public Opt(EscherRecordData erd) + { + super(erd); + numProperties = getInstance(); + readProperties(); + } + + /** + * Reads the properties + */ + private void readProperties() + { + properties = new ArrayList(); + int pos = 0; + byte[] bytes = getBytes(); + + for (int i = 0; i < numProperties; i++) + { + int val = IntegerHelper.getInt(bytes[pos], bytes[pos + 1]); + int id = val & 0x3fff; + int value = IntegerHelper.getInt(bytes[pos + 2], bytes[pos + 3], + bytes[pos + 4], bytes[pos + 5]); + Property p = new Property(id, + (val & 0x4000) != 0, + (val & 0x8000) != 0, + value); + pos += 6; + properties.add(p); + } + + for (Iterator i = properties.iterator(); i.hasNext();) + { + Property p = (Property) i.next(); + if (p.complex) + { + p.stringValue = StringHelper.getUnicodeString(bytes, p.value / 2, + pos); + pos += p.value; + } + } + } + + /** + * Constructor + */ + public Opt() + { + super(EscherRecordType.OPT); + properties = new ArrayList(); + setVersion(3); + } + + /** + * Accessor for the binary data + * + * @return the binary data + */ + byte[] getData() + { + numProperties = properties.size(); + setInstance(numProperties); + + data = new byte[numProperties * 6]; + int pos = 0; + + // Add in the root data + for (Iterator i = properties.iterator(); i.hasNext();) + { + Property p = (Property) i.next(); + int val = p.id & 0x3fff; + + if (p.blipId) + { + val |= 0x4000; + } + + if (p.complex) + { + val |= 0x8000; + } + + IntegerHelper.getTwoBytes(val, data, pos); + IntegerHelper.getFourBytes(p.value, data, pos + 2); + pos += 6; + } + + // Add in any complex data + for (Iterator i = properties.iterator(); i.hasNext();) + { + Property p = (Property) i.next(); + + if (p.complex && p.stringValue != null) + { + byte[] newData = + new byte[data.length + p.stringValue.length() * 2]; + System.arraycopy(data, 0, newData, 0, data.length); + StringHelper.getUnicodeBytes(p.stringValue, newData, data.length); + data = newData; + } + } + + return setHeaderData(data); + } + + /** + * Adds a property into the options + * + * @param id the property id + * @param blip the blip id + * @param complex whether it's a complex property + * @param val the value + */ + void addProperty(int id, boolean blip, boolean complex, int val) + { + Property p = new Property(id, blip, complex, val); + properties.add(p); + } + + /** + * Adds a property into the options + * + * @param id the property id + * @param blip the blip id + * @param complex whether it's a complex property + * @param val the value + * @param s the value string + */ + void addProperty(int id, boolean blip, boolean complex, int val, String s) + { + Property p = new Property(id, blip, complex, val, s); + properties.add(p); + } + + /** + * Accessor for the property + * + * @param id the property id + * @return the property + */ + Property getProperty(int id) + { + boolean found = false; + Property p = null; + for (Iterator i = properties.iterator(); i.hasNext() && !found;) + { + p = (Property) i.next(); + if (p.id == id) + { + found = true; + } + } + return found ? p : null; + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/Origin.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/Origin.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/Origin.java 17 Aug 2012 14:51:20 -0000 1.1 @@ -0,0 +1,37 @@ +/********************************************************************* +* +* Copyright (C) 2004 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +/** + * Enumeration type for the origin of a drawing object + */ +public final class Origin +{ + /** + * Constructor + */ + private Origin() + { + } + + public static final Origin READ = new Origin(); + public static final Origin WRITE = new Origin(); + public static final Origin READ_WRITE = new Origin(); +} Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/PNGReader.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/PNGReader.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/PNGReader.java 17 Aug 2012 14:51:19 -0000 1.1 @@ -0,0 +1,157 @@ +/********************************************************************* +* +* Copyright (C) 2006 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +import java.io.File; +import java.io.FileInputStream; +import java.util.Arrays; + +public class PNGReader +{ + private byte[] pngData; + + private Chunk ihdr; + private Chunk phys; + + private int pixelWidth; + private int pixelHeight; + private int verticalResolution; + private int horizontalResolution; + private int resolutionUnit; + + private static byte[] PNG_MAGIC_NUMBER = new byte[] + {(byte) 0x89, (byte) 0x50, (byte) 0x4e, (byte) 0x47, + (byte) 0x0d, (byte) 0x0a, (byte) 0x1a, (byte) 0x0a}; + + public PNGReader(byte[] data) + { + pngData = data; + } + + void read() + { + // Verify the magic data + byte[] header = new byte[PNG_MAGIC_NUMBER.length]; + System.arraycopy(pngData, 0, header, 0, header.length); + boolean pngFile = Arrays.equals(PNG_MAGIC_NUMBER, header); + if (!pngFile) + { + return; + } + + int pos = 8; + while (pos < pngData.length) + { + int length = getInt(pngData[pos], + pngData[pos+1], + pngData[pos+2], + pngData[pos+3]); + ChunkType chunkType = ChunkType.getChunkType(pngData[pos+4], + pngData[pos+5], + pngData[pos+6], + pngData[pos+7]); + + if (chunkType == ChunkType.IHDR) + { + ihdr = new Chunk(pos + 8, length, chunkType, pngData); + } + else if (chunkType == ChunkType.PHYS) + { + phys = new Chunk(pos + 8, length, chunkType, pngData); + } + + pos += length + 12; + } + + // Get the width and height from the ihdr + byte[] ihdrData = ihdr.getData(); + pixelWidth = getInt(ihdrData[0], ihdrData[1], ihdrData[2], ihdrData[3]); + pixelHeight = getInt(ihdrData[4], ihdrData[5], ihdrData[6], ihdrData[7]); + + if (phys != null) + { + byte[] physData = phys.getData(); + resolutionUnit = physData[8]; + horizontalResolution = getInt(physData[0], physData[1], + physData[2], physData[3]); + verticalResolution = getInt(physData[4], physData[5], + physData[6], physData[7]); + } + } + + // Gets the big-Endian integer + private int getInt(byte d1, byte d2, byte d3, byte d4) + { + int i1 = d1 & 0xff; + int i2 = d2 & 0xff; + int i3 = d3 & 0xff; + int i4 = d4 & 0xff; + + int val = i1 << 24 | + i2 << 16 | + i3 << 8 | + i4; + + return val; + } + + public int getHeight() + { + return pixelHeight; + } + + public int getWidth() + { + return pixelWidth; + } + + public int getHorizontalResolution() + { + // only return if the resolution unit is in metres + return resolutionUnit == 1 ? horizontalResolution : 0; + } + + public int getVerticalResolution() + { + // only return if the resolution unit is in metres + return resolutionUnit == 1 ? verticalResolution : 0; + } + + public static void main(String args[]) + { + try + { + File f = new File(args[0]); + int size = (int) f.length(); + + byte[] data = new byte[size]; + + FileInputStream fis = new FileInputStream(f); + fis.read(data); + fis.close(); + PNGReader reader = new PNGReader(data); + reader.read(); + } + catch (Throwable t) + { + t.printStackTrace(); + } + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/ShapeType.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/ShapeType.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/ShapeType.java 17 Aug 2012 14:51:19 -0000 1.1 @@ -0,0 +1,88 @@ +/********************************************************************* +* +* Copyright (C) 2004 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +/** + * Enumerations for the shape type + */ +final class ShapeType +{ + /** + * The value + */ + private int value; + + /** + * The list of shape types + */ + private static ShapeType[] types = new ShapeType[0]; + + /** + * Constructor + * + * @param v the value + */ + ShapeType(int v) + { + value = v; + + ShapeType[] old = types; + types = new ShapeType[types.length + 1]; + System.arraycopy(old, 0, types, 0, old.length); + types[old.length] = this; + } + + /** + * Gets the shape type given the value + * + * @param v the value + * @return the shape type for the value + */ + static ShapeType getType(int v) + { + ShapeType st = UNKNOWN; + boolean found = false; + for (int i = 0; i < types.length && !found; i++) + { + if (types[i].value == v) + { + found = true; + st = types[i]; + } + } + return st; + } + + /** + * Accessor for the value + * + * @return the value + */ + public int getValue() + { + return value; + } + + public static final ShapeType MIN = new ShapeType(0); + public static final ShapeType PICTURE_FRAME = new ShapeType(75); + public static final ShapeType HOST_CONTROL = new ShapeType(201); + public static final ShapeType TEXT_BOX = new ShapeType(202); + public static final ShapeType UNKNOWN = new ShapeType(-1); +} Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/SheetDrawingWriter.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/SheetDrawingWriter.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/SheetDrawingWriter.java 17 Aug 2012 14:51:19 -0000 1.1 @@ -0,0 +1,488 @@ +/********************************************************************* +* +* Copyright (C) 2004 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; + +import common.Logger; + +import jxl.WorkbookSettings; +import jxl.biff.IntegerHelper; +import jxl.write.biff.File; + +/** + * Handles the writing out of the different charts and images on a sheet. + * Called by the SheetWriter object + */ +public class SheetDrawingWriter +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(SheetDrawingWriter.class); + + /** + * The drawings on the sheet + */ + private ArrayList drawings; + + /** + * Flag indicating whether the drawings on the sheet were modified + */ + private boolean drawingsModified; + + /** + * The charts on the sheet + */ + private Chart[] charts; + + /** + * The workbook settings + */ + private WorkbookSettings workbookSettings; + + /** + * Constructor + * + * @param ws the workbook settings + */ + public SheetDrawingWriter(WorkbookSettings ws) + { + charts = new Chart[0]; + } + + /** + * The drawings on the sheet + * + * @param dr the list of drawings + * @param mod flag indicating whether the drawings have been tampered with + */ + public void setDrawings(ArrayList dr, boolean mod) + { + drawings = dr; + drawingsModified = mod; + } + + /** + * Writes out the MsoDrawing records and Obj records for each image + * and chart on the sheet + * + * @param outputFile the output file + * @exception IOException + */ + public void write(File outputFile) throws IOException + { + // If there are no drawings or charts on this sheet then exit + if (drawings.size() == 0 && charts.length == 0) + { + return; + } + + // See if any drawing has been modified + boolean modified = drawingsModified; + int numImages = drawings.size(); + + for (Iterator i = drawings.iterator(); i.hasNext() && !modified;) + { + DrawingGroupObject d = (DrawingGroupObject) i.next(); + if (d.getOrigin() != Origin.READ) + { + modified = true; + } + } + + // If the drawing order has been muddled at all, then we'll need + // to regenerate the Escher drawing data + if (numImages > 0 && !modified) + { + DrawingGroupObject d2 = (DrawingGroupObject) drawings.get(0); + if (!d2.isFirst()) + { + modified = true; + } + } + + // Check to see if this sheet consists of just a single chart. If so + // there is no MsoDrawingRecord, so write out the data and exit + if (numImages == 0 && + charts.length == 1 && + charts[0].getMsoDrawingRecord() == null) + { + modified = false; // this sheet has not been modified + } + + // If no drawing has been modified, then simply write them straight out + // again and exit + if (!modified) + { + writeUnmodified(outputFile); + return; + } + + Object[] spContainerData = new Object[numImages + charts.length]; + int length = 0; + EscherContainer firstSpContainer = null; + + // Get all the spContainer byte data from the drawings + // and store in an array + for (int i = 0; i < numImages; i++) + { + DrawingGroupObject drawing = (DrawingGroupObject) drawings.get(i); + + EscherContainer spc = drawing.getSpContainer(); + + if (spc != null) + { + byte[] data = spc.getData(); + spContainerData[i] = data; + + if (i == 0) + { + firstSpContainer = spc; + } + else + { + length += data.length; + } + } + } + + // Get all the spContainer bytes from the charts and add to the array + for (int i = 0; i < charts.length; i++) + { + EscherContainer spContainer = charts[i].getSpContainer(); + byte[] data = spContainer.getBytes(); //use getBytes instead of getData + data = spContainer.setHeaderData(data); + spContainerData[i + numImages] = data; + + if (i == 0 && numImages == 0) + { + firstSpContainer = spContainer; + } + else + { + length += data.length; + } + } + + // Put the generalised stuff around the first item + DgContainer dgContainer = new DgContainer(); + Dg dg = new Dg(numImages + charts.length); + dgContainer.add(dg); + + SpgrContainer spgrContainer = new SpgrContainer(); + + SpContainer spContainer = new SpContainer(); + Spgr spgr = new Spgr(); + spContainer.add(spgr); + Sp sp = new Sp(ShapeType.MIN, 1024, 5); + spContainer.add(sp); + spgrContainer.add(spContainer); + + spgrContainer.add(firstSpContainer); + + dgContainer.add(spgrContainer); + + byte[] firstMsoData = dgContainer.getData(); + + // Adjust the length of the DgContainer + int len = IntegerHelper.getInt(firstMsoData[4], + firstMsoData[5], + firstMsoData[6], + firstMsoData[7]); + IntegerHelper.getFourBytes(len + length, firstMsoData, 4); + + // Adjust the length of the SpgrContainer + len = IntegerHelper.getInt(firstMsoData[28], + firstMsoData[29], + firstMsoData[30], + firstMsoData[31]); + IntegerHelper.getFourBytes(len + length, firstMsoData, 28); + + // Now write out each MsoDrawing record + + // First MsoRecord + // test hack for form objects, to remove the ClientTextBox record + // from the end of the SpContainer + if (numImages > 0 && + ((DrawingGroupObject) drawings.get(0)).isFormObject()) + { + byte[] msodata2 = new byte[firstMsoData.length - 8]; + System.arraycopy(firstMsoData, 0, msodata2, 0, msodata2.length); + firstMsoData = msodata2; + } + + MsoDrawingRecord msoDrawingRecord = new MsoDrawingRecord(firstMsoData); + outputFile.write(msoDrawingRecord); + + if (numImages > 0) + { + DrawingGroupObject firstDrawing = (DrawingGroupObject) drawings.get(0); + firstDrawing.writeAdditionalRecords(outputFile); + } + else + { + // first image is a chart + Chart chart = charts[0]; + ObjRecord objRecord = chart.getObjRecord(); + outputFile.write(objRecord); + outputFile.write(chart); + } + + // Now do all the others + for (int i = 1; i < spContainerData.length; i++) + { + byte[] bytes = (byte[]) spContainerData[i]; + + // test hack for form objects, to remove the ClientTextBox record + // from the end of the SpContainer + if (i < numImages && + ((DrawingGroupObject) drawings.get(i)).isFormObject()) + { + byte[] bytes2 = new byte[bytes.length - 8]; + System.arraycopy(bytes, 0, bytes2, 0, bytes2.length); + bytes = bytes2; + } + + msoDrawingRecord = new MsoDrawingRecord(bytes); + outputFile.write(msoDrawingRecord); + + if (i < numImages) + { + // Write anything else the object needs + DrawingGroupObject d = (DrawingGroupObject) drawings.get(i); + d.writeAdditionalRecords(outputFile); + } + else + { + Chart chart = charts[i - numImages]; + ObjRecord objRecord = chart.getObjRecord(); + outputFile.write(objRecord); + outputFile.write(chart); + } + } + + // Write any tail records that need to be written + for (Iterator i = drawings.iterator(); i.hasNext();) + { + DrawingGroupObject dgo2 = (DrawingGroupObject) i.next(); + dgo2.writeTailRecords(outputFile); + } + } + + /** + * Writes out the drawings and the charts if nothing has been modified + * + * @param outputFile the output file + * @exception IOException + */ + private void writeUnmodified(File outputFile) throws IOException + { + if (charts.length == 0 && drawings.size() == 0) + { + // No drawings or charts + return; + } + else if (charts.length == 0 && drawings.size() != 0) + { + // If there are no charts, then write out the drawings and return + for (Iterator i = drawings.iterator(); i.hasNext();) + { + DrawingGroupObject d = (DrawingGroupObject) i.next(); + outputFile.write(d.getMsoDrawingRecord()); + d.writeAdditionalRecords(outputFile); + } + + for (Iterator i = drawings.iterator(); i.hasNext();) + { + DrawingGroupObject d = (DrawingGroupObject) i.next(); + d.writeTailRecords(outputFile); + } + return; + } + else if (drawings.size() == 0 && charts.length != 0) + { + // If there are no drawings, then write out the charts and return + Chart curChart = null; + for (int i = 0; i < charts.length; i++) + { + curChart = charts[i]; + if (curChart.getMsoDrawingRecord() != null) + { + outputFile.write(curChart.getMsoDrawingRecord()); + } + + if (curChart.getObjRecord() != null) + { + outputFile.write(curChart.getObjRecord()); + } + + outputFile.write(curChart); + } + + return; + } + + // There are both charts and drawings - the output + // drawing group records will need + // to be re-jigged in order to write the drawings out first, then the + // charts + int numDrawings = drawings.size(); + int length = 0; + EscherContainer[] spContainers = + new EscherContainer[numDrawings + charts.length]; + boolean[] isFormObject = new boolean[numDrawings + charts.length]; + + for (int i = 0; i < numDrawings; i++) + { + DrawingGroupObject d = (DrawingGroupObject) drawings.get(i); + spContainers[i] = d.getSpContainer(); + + if (i > 0) + { + length += spContainers[i].getLength(); + } + + if (d.isFormObject()) + { + isFormObject[i] = true; + } + } + + for (int i = 0; i < charts.length; i++) + { + spContainers[i + numDrawings] = charts[i].getSpContainer(); + length += spContainers[i + numDrawings].getLength(); + } + + // Put the generalised stuff around the first item + DgContainer dgContainer = new DgContainer(); + Dg dg = new Dg(numDrawings + charts.length); + dgContainer.add(dg); + + SpgrContainer spgrContainer = new SpgrContainer(); + + SpContainer spContainer = new SpContainer(); + Spgr spgr = new Spgr(); + spContainer.add(spgr); + Sp sp = new Sp(ShapeType.MIN, 1024, 5); + spContainer.add(sp); + spgrContainer.add(spContainer); + + spgrContainer.add(spContainers[0]); + + dgContainer.add(spgrContainer); + + byte[] firstMsoData = dgContainer.getData(); + + // Adjust the length of the DgContainer + int len = IntegerHelper.getInt(firstMsoData[4], + firstMsoData[5], + firstMsoData[6], + firstMsoData[7]); + IntegerHelper.getFourBytes(len + length, firstMsoData, 4); + + // Adjust the length of the SpgrContainer + len = IntegerHelper.getInt(firstMsoData[28], + firstMsoData[29], + firstMsoData[30], + firstMsoData[31]); + IntegerHelper.getFourBytes(len + length, firstMsoData, 28); + + // Now write out each MsoDrawing record and object record + + // Hack to remove the last eight bytes (text box escher record) + // from the container + if (isFormObject[0] == true) + { + byte[] cbytes = new byte[firstMsoData.length - 8]; + System.arraycopy(firstMsoData, 0, cbytes, 0, cbytes.length); + firstMsoData = cbytes; + } + + // First MsoRecord + MsoDrawingRecord msoDrawingRecord = new MsoDrawingRecord(firstMsoData); + outputFile.write(msoDrawingRecord); + + DrawingGroupObject dgo = (DrawingGroupObject) drawings.get(0); + dgo.writeAdditionalRecords(outputFile); + + // Now do all the others + for (int i = 1; i < spContainers.length; i++) + { + byte[] bytes = spContainers[i].getBytes(); + byte[] bytes2 = spContainers[i].setHeaderData(bytes); + + // Hack to remove the last eight bytes (text box escher record) + // from the container + if (isFormObject[i] == true) + { + byte[] cbytes = new byte[bytes2.length - 8]; + System.arraycopy(bytes2, 0, cbytes, 0, cbytes.length); + bytes2 = cbytes; + } + + msoDrawingRecord = new MsoDrawingRecord(bytes2); + outputFile.write(msoDrawingRecord); + + if (i < numDrawings) + { + dgo = (DrawingGroupObject) drawings.get(i); + dgo.writeAdditionalRecords(outputFile); + } + else + { + Chart chart = charts[i - numDrawings]; + ObjRecord objRecord = chart.getObjRecord(); + outputFile.write(objRecord); + outputFile.write(chart); + } + } + + // Write any tail records that need to be written + for (Iterator i = drawings.iterator(); i.hasNext();) + { + DrawingGroupObject dgo2 = (DrawingGroupObject) i.next(); + dgo2.writeTailRecords(outputFile); + } + } + + /** + * Sets the charts on the sheet + * + * @param ch the charts + */ + public void setCharts(Chart[] ch) + { + charts = ch; + } + + /** + * Accessor for the charts on the sheet + * + * @return the charts + */ + public Chart[] getCharts() + { + return charts; + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/Sp.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/Sp.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/Sp.java 17 Aug 2012 14:51:20 -0000 1.1 @@ -0,0 +1,120 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +import common.Logger; + +import jxl.biff.IntegerHelper; + +/** + * The Sp escher atom + */ +class Sp extends EscherAtom +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(Sp.class); + + /** + * The binary data + */ + private byte[] data; + + /** + * The shape type + */ + private int shapeType; + + /** + * The shape id + */ + private int shapeId; + + /** + * The Sp persistence flags + */ + private int persistenceFlags; + + /** + * Constructor + * + * @param erd the entity record data + */ + public Sp(EscherRecordData erd) + { + super(erd); + shapeType = getInstance(); + byte[] bytes = getBytes(); + shapeId = IntegerHelper.getInt(bytes[0], bytes[1], bytes[2], bytes[3]); + persistenceFlags = IntegerHelper.getInt(bytes[4], bytes[5], + bytes[6], bytes[7]); + } + + /** + * Constructor - used when writing + * + * @param st the shape type + * @param sid the shape id + * @param p persistence flags + */ + public Sp(ShapeType st, int sid, int p) + { + super(EscherRecordType.SP); + setVersion(2); + shapeType = st.getValue(); + shapeId = sid; + persistenceFlags = p; + setInstance(shapeType); + } + + /** + * Accessor for the shape id + * + * @return the shape id + */ + int getShapeId() + { + return shapeId; + } + + /** + * Accessor for the shape type + * + * @return the shape type + */ + int getShapeType() + { + return shapeType; + } + + /** + * Gets the data + * + * @return the binary data + */ + byte[] getData() + { + data = new byte[8]; + IntegerHelper.getFourBytes(shapeId, data, 0); + IntegerHelper.getFourBytes(persistenceFlags, data, 4); + return setHeaderData(data); + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/SpContainer.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/SpContainer.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/SpContainer.java 17 Aug 2012 14:51:19 -0000 1.1 @@ -0,0 +1,44 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +/** + * Shape contianer - Contains the data for this particular shape + */ +class SpContainer extends EscherContainer +{ + /** + * Constructor + */ + public SpContainer() + { + super(EscherRecordType.SP_CONTAINER); + } + + /** + * Constructor + * + * @param erd the escher record data + */ + public SpContainer(EscherRecordData erd) + { + super(erd); + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/Spgr.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/Spgr.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/Spgr.java 17 Aug 2012 14:51:19 -0000 1.1 @@ -0,0 +1,61 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +/** + * The SpGr escher atom + */ +class Spgr extends EscherAtom +{ + /** + * The binary data + */ + private byte[] data; + + /** + * Constructor + * + * @param erd the raw escher record data + */ + public Spgr(EscherRecordData erd) + { + super(erd); + } + + /** + * Constructor + */ + public Spgr() + { + super(EscherRecordType.SPGR); + setVersion(1); + data = new byte[16]; + } + + /** + * Gets the binary data + * + * @return the binary data + */ + byte[] getData() + { + return setHeaderData(data); + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/SpgrContainer.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/SpgrContainer.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/SpgrContainer.java 17 Aug 2012 14:51:19 -0000 1.1 @@ -0,0 +1,51 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +import common.Logger; + +/** + * An Spgr container record in an escher stream + */ +class SpgrContainer extends EscherContainer +{ + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(SpgrContainer.class); + + /** + * Constructor used when writing + */ + public SpgrContainer() + { + super(EscherRecordType.SPGR_CONTAINER); + } + + /** + * Constructor + * + * @param erd the escher record data read in + */ + public SpgrContainer(EscherRecordData erd) + { + super(erd); + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/SplitMenuColors.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/SplitMenuColors.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/SplitMenuColors.java 17 Aug 2012 14:51:20 -0000 1.1 @@ -0,0 +1,67 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +/** + * Split menu colours escher record + */ +class SplitMenuColors extends EscherAtom +{ + /** + * The binary data + */ + private byte[] data; + + /** + * Constructor + * + * @param erd escher record data + */ + public SplitMenuColors(EscherRecordData erd) + { + super(erd); + } + + /** + * Constructor + */ + public SplitMenuColors() + { + super(EscherRecordType.SPLIT_MENU_COLORS); + setVersion(0); + setInstance(4); + + data = new byte[] + {(byte) 0x0d, (byte) 0x00, (byte) 0x00, (byte) 0x08, + (byte) 0x0c, (byte) 0x00, (byte) 0x00, (byte) 0x08, + (byte) 0x17, (byte) 0x00, (byte) 0x00, (byte) 0x08, + (byte) 0xf7, (byte) 0x00, (byte) 0x00, (byte) 0x10}; + } + + /** + * The binary data + * + * @return the binary data + */ + byte[] getData() + { + return setHeaderData(data); + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/drawing/TextObjectRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/drawing/TextObjectRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/drawing/TextObjectRecord.java 17 Aug 2012 14:51:20 -0000 1.1 @@ -0,0 +1,120 @@ +/********************************************************************* +* +* Copyright (C) 2004 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.drawing; + +import common.Logger; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; +import jxl.read.biff.Record; + +/** + * A TextObject (TXO) record which contains the information for comments + */ +public class TextObjectRecord extends WritableRecordData +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(TextObjectRecord.class); + + /** + * The raw drawing data which was read in + */ + private byte[] data; + + /** + * The text + */ + private int textLength; + + /** + * Constructor invoked when writing out this object + * + * @param t the text string + */ + TextObjectRecord(String t) + { + super(Type.TXO); + + textLength = t.length(); + } + + /** + * Constructs this object from the raw data + * + * @param t the raw data + */ + public TextObjectRecord(Record t) + { + super(t); + data = getRecord().getData(); + textLength = IntegerHelper.getInt(data[10], data[11]); + } + + /** + * Constructor + * + * @param d the drawing data + */ + public TextObjectRecord(byte[] d) + { + super(Type.TXO); + data = d; + } + + /** + * Expose the protected function to the SheetImpl in this package + * + * @return the raw record data + */ + public byte[] getData() + { + if (data != null) + { + return data; + } + + data = new byte[18]; + + // the options + int options = 0; + options |= (0x1 << 1); // horizontal alignment - left + options |= (0x1 << 4); // vertical alignment - top + options |= (0x1 << 9); // lock text + IntegerHelper.getTwoBytes(options, data, 0); + + // the rotation + // no rotation + + // Length of text + IntegerHelper.getTwoBytes(textLength, data, 10); + + // Length of formatting runs + IntegerHelper.getTwoBytes(0x10, data, 12); + + return data; + } +} + + + + Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/Add.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/Add.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/Add.java 17 Aug 2012 14:51:15 -0000 1.1 @@ -0,0 +1,64 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +/** + * A cell reference in a formula + */ +class Add extends BinaryOperator implements ParsedThing +{ + /** + * Constructor + */ + public Add() + { + } + + /** + * Gets the symbol for string displays + * + * @return the symbol + */ + public String getSymbol() + { + return "+"; + } + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + Token getToken() + { + return Token.ADD; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() + { + return 4; + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/Area.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/Area.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/Area.java 17 Aug 2012 14:51:14 -0000 1.1 @@ -0,0 +1,423 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +import common.Assert; +import common.Logger; + +import jxl.biff.CellReferenceHelper; +import jxl.biff.IntegerHelper; + +/** + * A nested class to hold range information + */ +class Area extends Operand implements ParsedThing +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(Area.class); + + /** + * The first column + */ + private int columnFirst; + + /** + * The first row + */ + private int rowFirst; + + /** + * The last column + */ + private int columnLast; + + /** + * The last row + */ + private int rowLast; + + /** + * Indicates whether the first column is a relative or absolute reference + */ + private boolean columnFirstRelative; + + /** + * Indicates whether the first row is a relative or absolute reference + */ + private boolean rowFirstRelative; + + /** + * Indicates whether the last column is a relative or absolute reference + */ + private boolean columnLastRelative; + + /** + * Indicates whether the last row is a relative or absolute reference + */ + private boolean rowLastRelative; + + /** + * Constructor + */ + Area() + { + } + + /** + * Constructor invoked when parsing a string formula + * + * @param s the string to parse + */ + Area(String s) + { + int seppos = s.indexOf(":"); + Assert.verify(seppos != -1); + String startcell = s.substring(0, seppos); + String endcell = s.substring(seppos + 1); + + columnFirst = CellReferenceHelper.getColumn(startcell); + rowFirst = CellReferenceHelper.getRow(startcell); + columnLast = CellReferenceHelper.getColumn(endcell); + rowLast = CellReferenceHelper.getRow(endcell); + + columnFirstRelative = CellReferenceHelper.isColumnRelative(startcell); + rowFirstRelative = CellReferenceHelper.isRowRelative(startcell); + columnLastRelative = CellReferenceHelper.isColumnRelative(endcell); + rowLastRelative = CellReferenceHelper.isRowRelative(endcell); + } + + /** + * Accessor for the first column + * + * @return the first column + */ + int getFirstColumn() + { + return columnFirst; + } + + /** + * Accessor for the first row + * + * @return the first row + */ + int getFirstRow() + { + return rowFirst; + } + + /** + * Accessor for the last column + * + * @return the last column + */ + int getLastColumn() + { + return columnLast; + } + + /** + * Accessor for the last row + * + * @return the last row + */ + int getLastRow() + { + return rowLast; + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) + { + rowFirst = IntegerHelper.getInt(data[pos], data[pos + 1]); + rowLast = IntegerHelper.getInt(data[pos + 2], data[pos + 3]); + int columnMask = IntegerHelper.getInt(data[pos + 4], data[pos + 5]); + columnFirst = columnMask & 0x00ff; + columnFirstRelative = ((columnMask & 0x4000) != 0); + rowFirstRelative = ((columnMask & 0x8000) != 0); + columnMask = IntegerHelper.getInt(data[pos + 6], data[pos + 7]); + columnLast = columnMask & 0x00ff; + columnLastRelative = ((columnMask & 0x4000) != 0); + rowLastRelative = ((columnMask & 0x8000) != 0); + + return 8; + } + + /** + * Gets the string representation of this item + * + * @param buf the string buffer + */ + public void getString(StringBuffer buf) + { + CellReferenceHelper.getCellReference(columnFirst, rowFirst, buf); + buf.append(':'); + CellReferenceHelper.getCellReference(columnLast, rowLast, buf); + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() + { + byte[] data = new byte[9]; + data[0] = !useAlternateCode() ? Token.AREA.getCode() : + Token.AREA.getCode2(); + + IntegerHelper.getTwoBytes(rowFirst, data, 1); + IntegerHelper.getTwoBytes(rowLast, data, 3); + + int grcol = columnFirst; + + // Set the row/column relative bits if applicable + if (rowFirstRelative) + { + grcol |= 0x8000; + } + + if (columnFirstRelative) + { + grcol |= 0x4000; + } + + IntegerHelper.getTwoBytes(grcol, data, 5); + + grcol = columnLast; + + // Set the row/column relative bits if applicable + if (rowLastRelative) + { + grcol |= 0x8000; + } + + if (columnLastRelative) + { + grcol |= 0x4000; + } + + IntegerHelper.getTwoBytes(grcol, data, 7); + + return data; + } + + /** + * Adjusts all the relative cell references in this formula by the + * amount specified. Used when copying formulas + * + * @param colAdjust the amount to add on to each relative cell reference + * @param rowAdjust the amount to add on to each relative row reference + */ + public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) + { + if (columnFirstRelative) + { + columnFirst += colAdjust; + } + + if (columnLastRelative) + { + columnLast += colAdjust; + } + + if (rowFirstRelative) + { + rowFirst += rowAdjust; + } + + if (rowLastRelative) + { + rowLast += rowAdjust; + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param col the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnInserted(int sheetIndex, int col, boolean currentSheet) + { + if (!currentSheet) + { + return; + } + + if (col <= columnFirst) + { + columnFirst++; + } + + if (col <= columnLast) + { + columnLast++; + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param col the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnRemoved(int sheetIndex, int col, boolean currentSheet) + { + if (!currentSheet) + { + return; + } + + if (col < columnFirst) + { + columnFirst--; + } + + if (col <= columnLast) + { + columnLast--; + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was inserted + * @param row the row number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowInserted(int sheetIndex, int row, boolean currentSheet) + { + if (!currentSheet) + { + return; + } + + if (rowLast == 0xffff) + { + // area applies to the whole column, so nothing to do + return; + } + + if (row <= rowFirst) + { + rowFirst++; + } + + if (row <= rowLast) + { + rowLast++; + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was removed + * @param row the row number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowRemoved(int sheetIndex, int row, boolean currentSheet) + { + if (!currentSheet) + { + return; + } + + if (rowLast == 0xffff) + { + // area applies to the whole column, so nothing to do + return; + } + + if (row < rowFirst) + { + rowFirst--; + } + + if (row <= rowLast) + { + rowLast--; + } + } + + /** + * Used by subclasses columns/row range to set the range information + * + * @param colFirst the first column + * @param colLast the last column + * @param rwFirst the first row + * @param rwLast the last row + * @param colFirstRel flag indicating whether the first column is relative + * @param colLastRel flag indicating whether the last column is relative + * @param rowFirstRel flag indicating whether the first row is relative + * @param rowLastRel flag indicating whether the last row is relative + */ + protected void setRangeData(int colFirst, + int colLast, + int rwFirst, + int rwLast, + boolean colFirstRel, + boolean colLastRel, + boolean rowFirstRel, + boolean rowLastRel) + { + columnFirst = colFirst; + columnLast = colLast; + rowFirst = rwFirst; + rowLast = rwLast; + columnFirstRelative = colFirstRel; + columnLastRelative = colLastRel; + rowFirstRelative = rowFirstRel; + rowLastRelative = rowLastRel; + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing here + */ + void handleImportedCellReferences() + { + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/Area3d.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/Area3d.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/Area3d.java 17 Aug 2012 14:51:15 -0000 1.1 @@ -0,0 +1,475 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +import common.Assert; +import common.Logger; + +import jxl.biff.CellReferenceHelper; +import jxl.biff.IntegerHelper; + +/** + * A nested class to hold range information + */ +class Area3d extends Operand implements ParsedThing +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(Area3d.class); + + /** + * The sheet + */ + private int sheet; + + /** + * The first column + */ + private int columnFirst; + + /** + * The first row + */ + private int rowFirst; + + /** + * The last column + */ + private int columnLast; + + /** + * The last row + */ + private int rowLast; + + /** + * Indicates whether the first column is relative + */ + private boolean columnFirstRelative; + + /** + * Indicates whether the first row is relative + */ + private boolean rowFirstRelative; + + /** + * Indicates whether the last column is relative + */ + private boolean columnLastRelative; + + /** + * Indicates whether the last row is relative + */ + private boolean rowLastRelative; + + /** + * A handle to the workbook + */ + private ExternalSheet workbook; + + /** + * Constructor + * + * @param es the external sheet + */ + Area3d(ExternalSheet es) + { + workbook = es; + } + + /** + * Constructor invoked when parsing a string formula + * + * @param s the string to parse + * @param es the external sheet + * @exception FormulaException + */ + Area3d(String s, ExternalSheet es) throws FormulaException + { + workbook = es; + int seppos = s.lastIndexOf(":"); + Assert.verify(seppos != -1); + String endcell = s.substring(seppos + 1); + + // Get the the start cell details + int sep = s.indexOf('!'); + String cellString = s.substring(sep + 1, seppos); + columnFirst = CellReferenceHelper.getColumn(cellString); + rowFirst = CellReferenceHelper.getRow(cellString); + + // Get the sheet index + String sheetName = s.substring(0, sep); + + // Remove single quotes, if they exist + if (sheetName.charAt(0) == '\'' && + sheetName.charAt(sheetName.length() - 1) == '\'') + { + sheetName = sheetName.substring(1, sheetName.length() - 1); + } + + sheet = es.getExternalSheetIndex(sheetName); + + if (sheet < 0) + { + throw new FormulaException(FormulaException.SHEET_REF_NOT_FOUND, + sheetName); + } + + // Get the last cell index + columnLast = CellReferenceHelper.getColumn(endcell); + rowLast = CellReferenceHelper.getRow(endcell); + + columnFirstRelative = true; + rowFirstRelative = true; + columnLastRelative = true; + rowLastRelative = true; + } + + /** + * Accessor for the first column + * + * @return the first column + */ + int getFirstColumn() + { + return columnFirst; + } + + /** + * Accessor for the first row + * + * @return the first row + */ + int getFirstRow() + { + return rowFirst; + } + + /** + * Accessor for the last column + * + * @return the last column + */ + int getLastColumn() + { + return columnLast; + } + + /** + * Accessor for the last row + * + * @return the last row + */ + int getLastRow() + { + return rowLast; + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) + { + sheet = IntegerHelper.getInt(data[pos], data[pos + 1]); + rowFirst = IntegerHelper.getInt(data[pos + 2], data[pos + 3]); + rowLast = IntegerHelper.getInt(data[pos + 4], data[pos + 5]); + int columnMask = IntegerHelper.getInt(data[pos + 6], data[pos + 7]); + columnFirst = columnMask & 0x00ff; + columnFirstRelative = ((columnMask & 0x4000) != 0); + rowFirstRelative = ((columnMask & 0x8000) != 0); + columnMask = IntegerHelper.getInt(data[pos + 8], data[pos + 9]); + columnLast = columnMask & 0x00ff; + columnLastRelative = ((columnMask & 0x4000) != 0); + rowLastRelative = ((columnMask & 0x8000) != 0); + + return 10; + } + + /** + * Gets the string version of this area + * + * @param buf the area to populate + */ + public void getString(StringBuffer buf) + { + CellReferenceHelper.getCellReference + (sheet, columnFirst, rowFirst, workbook, buf); + buf.append(':'); + CellReferenceHelper.getCellReference(columnLast, rowLast, buf); + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() + { + byte[] data = new byte[11]; + data[0] = Token.AREA3D.getCode(); + + IntegerHelper.getTwoBytes(sheet, data, 1); + + IntegerHelper.getTwoBytes(rowFirst, data, 3); + IntegerHelper.getTwoBytes(rowLast, data, 5); + + int grcol = columnFirst; + + // Set the row/column relative bits if applicable + if (rowFirstRelative) + { + grcol |= 0x8000; + } + + if (columnFirstRelative) + { + grcol |= 0x4000; + } + + IntegerHelper.getTwoBytes(grcol, data, 7); + + grcol = columnLast; + + // Set the row/column relative bits if applicable + if (rowLastRelative) + { + grcol |= 0x8000; + } + + if (columnLastRelative) + { + grcol |= 0x4000; + } + + IntegerHelper.getTwoBytes(grcol, data, 9); + + return data; + } + + /** + * Adjusts all the relative cell references in this formula by the + * amount specified. Used when copying formulas + * + * @param colAdjust the amount to add on to each relative cell reference + * @param rowAdjust the amount to add on to each relative row reference + */ + public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) + { + if (columnFirstRelative) + { + columnFirst += colAdjust; + } + + if (columnLastRelative) + { + columnLast += colAdjust; + } + + if (rowFirstRelative) + { + rowFirst += rowAdjust; + } + + if (rowLastRelative) + { + rowLast += rowAdjust; + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param col the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + public void columnInserted(int sheetIndex, int col, boolean currentSheet) + { + if (sheetIndex != sheet) + { + return; + } + + if (columnFirst >= col) + { + columnFirst++; + } + + if (columnLast >= col) + { + columnLast++; + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param col the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnRemoved(int sheetIndex, int col, boolean currentSheet) + { + if (sheetIndex != sheet) + { + return; + } + + if (col < columnFirst) + { + columnFirst--; + } + + if (col <= columnLast) + { + columnLast--; + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was inserted + * @param row the row number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowInserted(int sheetIndex, int row, boolean currentSheet) + { + if (sheetIndex != sheet) + { + return; + } + + if (rowLast == 0xffff) + { + // area applies to the whole column, so nothing to do + return; + } + + if (row <= rowFirst) + { + rowFirst++; + } + + if (row <= rowLast) + { + rowLast++; + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was removed + * @param row the row number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowRemoved(int sheetIndex, int row, boolean currentSheet) + { + if (sheetIndex != sheet) + { + return; + } + + if (rowLast == 0xffff) + { + // area applies to the whole column, so nothing to do + return; + } + + if (row < rowFirst) + { + rowFirst--; + } + + if (row <= rowLast) + { + rowLast--; + } + } + + /** + * Used by subclasses columns/row range to set the range information + * + * @param sht the sheet containing the area + * @param colFirst the first column + * @param colLast the last column + * @param rwFirst the first row + * @param rwLast the last row + * @param colFirstRel flag indicating whether the first column is relative + * @param colLastRel flag indicating whether the last column is relative + * @param rowFirstRel flag indicating whether the first row is relative + * @param rowLastRel flag indicating whether the last row is relative + */ + protected void setRangeData(int sht, + int colFirst, + int colLast, + int rwFirst, + int rwLast, + boolean colFirstRel, + boolean colLastRel, + boolean rowFirstRel, + boolean rowLastRel) + { + sheet = sht; + columnFirst = colFirst; + columnLast = colLast; + rowFirst = rwFirst; + rowLast = rwLast; + columnFirstRelative = colFirstRel; + columnLastRelative = colLastRel; + rowFirstRelative = rowFirstRel; + rowLastRelative = rowLastRel; + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + */ + void handleImportedCellReferences() + { + setInvalid(); + } +} + + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/ArgumentSeparator.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/ArgumentSeparator.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/ArgumentSeparator.java 17 Aug 2012 14:51:15 -0000 1.1 @@ -0,0 +1,34 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +/** + * A dummy token used when parsing strings in order to indicate the + * separation of parameters + */ +class ArgumentSeparator extends StringParseItem +{ + /** + * Constructor + */ + public ArgumentSeparator() + { + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/Attribute.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/Attribute.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/Attribute.java 17 Aug 2012 14:51:17 -0000 1.1 @@ -0,0 +1,547 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +import java.util.Stack; + +import common.Logger; + +import jxl.WorkbookSettings; +import jxl.biff.IntegerHelper; + +/** + * A special attribute control token - typically either a SUM function + * or an IF function + */ +class Attribute extends Operator implements ParsedThing +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(Attribute.class); + + /** + * The options used by the attribute + */ + private int options; + + /** + * The word contained in this attribute + */ + private int word; + + /** + * The workbook settings + */ + private WorkbookSettings settings; + + private static final int SUM_MASK = 0x10; + private static final int IF_MASK = 0x02; + private static final int CHOOSE_MASK = 0x04; + private static final int GOTO_MASK = 0x08; + + /** + * If this attribute is an IF functions, sets the associated if conditions + */ + private VariableArgFunction ifConditions; + + /** + * Constructor + * + * @param ws the workbook settings + */ + public Attribute(WorkbookSettings ws) + { + settings = ws; + } + + /** + * Constructor for use when this is called when parsing a string + * + * @param sf the built in function + * @param ws the workbook settings + */ + public Attribute(StringFunction sf, WorkbookSettings ws) + { + settings = ws; + + if (sf.getFunction(settings) == Function.SUM) + { + options |= SUM_MASK; + } + else if (sf.getFunction(settings) == Function.IF) + { + options |= IF_MASK; + } + } + + /** + * Sets the if conditions for this attribute, if it represents an IF function + * + * @param vaf a VariableArgFunction value + */ + void setIfConditions(VariableArgFunction vaf) + { + ifConditions = vaf; + + // Sometimes there is not Attribute token, so we need to create + // an attribute out of thin air. In that case, make sure the if mask + options |= IF_MASK; + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) + { + options = data[pos]; + word = IntegerHelper.getInt(data[pos + 1], data[pos + 2]); + + if (!isChoose()) + { + return 3; + } + + // word contains the number of jumps by index. + // and there is an additional final jump to the choose function itself. + return 3 + (word + 1) * 2; + } + + /** + * Queries whether this attribute is a function + * + * @return TRUE if this is a function, FALSE otherwise + */ + public boolean isFunction() + { + return (options & (SUM_MASK | IF_MASK)) != 0; + } + + /** + * Queries whether this attribute is a sum + * + * @return TRUE if this is SUM, FALSE otherwise + */ + public boolean isSum() + { + return (options & SUM_MASK) != 0; + } + + /** + * Queries whether this attribute is an IF + * + * @return TRUE if this is an IF, FALSE otherwise + */ + public boolean isIf() + { + return (options & IF_MASK) != 0; + } + + /** + * Queries whether this attribute is a goto + * + * @return TRUE if this is a goto, FALSE otherwise + */ + public boolean isGoto() + { + return (options & GOTO_MASK) != 0; + } + + /** + * Queries whether this attribute is a CHOOSE + * + * @return TRUE if this is a CHOOSE, FALSE otherwise + */ + public boolean isChoose() + { + return (options & CHOOSE_MASK) != 0; + } + + /** + * Gets the operands for this operator from the stack + * + * @param s the token stack + */ + public void getOperands(Stack s) + { + if ((options & SUM_MASK) != 0) + { + ParseItem o1 = (ParseItem) s.pop(); + add(o1); + } + else if ((options & IF_MASK) != 0) + { + ParseItem o1 = (ParseItem) s.pop(); + add(o1); + } + } + + /** + * Gets the string version of the attribute + * + * @param buf the buffer to populate + */ + public void getString(StringBuffer buf) + { + if ((options & SUM_MASK) != 0) + { + ParseItem[] operands = getOperands(); + buf.append(Function.SUM.getName(settings)); + buf.append('('); + operands[0].getString(buf); + buf.append(')'); + } + else if ((options & IF_MASK) != 0) + { + buf.append(Function.IF.getName(settings)); + buf.append('('); + + ParseItem[] operands = ifConditions.getOperands(); + + // Operands are in the correct order for IFs + for (int i = 0; i < operands.length - 1; i++) + { + operands[i].getString(buf); + buf.append(','); + } + operands[operands.length - 1].getString(buf); + buf.append(')'); + } + } + + /** + * Gets the token representation of this item in RPN. The Attribute + * token is a special case, which overrides anything useful we could do + * in the base class + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() + { + byte[] data = new byte[0]; + if (isSum()) + { + // Get the data for the operands + ParseItem[] operands = getOperands(); + + // Get the operands in reverse order to get the RPN + for (int i = operands.length - 1; i >= 0; i--) + { + byte[] opdata = operands[i].getBytes(); + + // Grow the array + byte[] newdata = new byte[data.length + opdata.length]; + System.arraycopy(data, 0, newdata, 0, data.length); + System.arraycopy(opdata, 0, newdata, data.length, opdata.length); + data = newdata; + } + + // Add on the operator byte + byte[] newdata = new byte[data.length + 4]; + System.arraycopy(data, 0, newdata, 0, data.length); + newdata[data.length] = Token.ATTRIBUTE.getCode(); + newdata[data.length + 1] = SUM_MASK; + data = newdata; + } + else if (isIf()) + { + return getIf(); + } + + return data; + } + + /** + * Gets the associated if conditions with this attribute + * + * @return the associated if conditions + */ + private byte[] getIf() + { + ParseItem[] operands = ifConditions.getOperands(); + + // The position of the offset to the false portion of the expression + int falseOffsetPos = 0; + int gotoEndPos = 0; + int numArgs = operands.length; + + // First, write out the conditions + byte[] data = operands[0].getBytes(); + + // Grow the array by three and write out the optimized if attribute + int pos = data.length; + byte[] newdata = new byte[data.length + 4]; + System.arraycopy(data, 0, newdata, 0, data.length); + data = newdata; + data[pos] = Token.ATTRIBUTE.getCode(); + data[pos + 1] = 0x2; + falseOffsetPos = pos + 2; + + // Get the true portion of the expression and add it to the array + byte[] truedata = operands[1].getBytes(); + newdata = new byte[data.length + truedata.length]; + System.arraycopy(data, 0, newdata, 0, data.length); + System.arraycopy(truedata, 0, newdata, data.length, truedata.length); + data = newdata; + + // Grow the array by three and write out the goto end attribute + pos = data.length; + newdata = new byte[data.length + 4]; + System.arraycopy(data, 0, newdata, 0, data.length); + data = newdata; + data[pos] = Token.ATTRIBUTE.getCode(); + data[pos + 1] = 0x8; + gotoEndPos = pos + 2; + + // If the false condition exists, then add that to the array + if (numArgs > 2) + { + // Set the offset to the false expression to be the current position + IntegerHelper.getTwoBytes(data.length - falseOffsetPos - 2, + data, falseOffsetPos); + + // Copy in the false expression + byte[] falsedata = operands[numArgs - 1].getBytes(); + newdata = new byte[data.length + falsedata.length]; + System.arraycopy(data, 0, newdata, 0, data.length); + System.arraycopy(falsedata, 0, newdata, data.length, falsedata.length); + data = newdata; + + // Write the goto to skip over the varargs token + pos = data.length; + newdata = new byte[data.length + 4]; + System.arraycopy(data, 0, newdata, 0, data.length); + data = newdata; + data[pos] = Token.ATTRIBUTE.getCode(); + data[pos + 1] = 0x8; + data[pos + 2] = 0x3; + } + + // Grow the array and write out the varargs function + pos = data.length; + newdata = new byte[data.length + 4]; + System.arraycopy(data, 0, newdata, 0, data.length); + data = newdata; + data[pos] = Token.FUNCTIONVARARG.getCode(); + data[pos + 1] = (byte) numArgs; + data[pos + 2] = 1; + data[pos + 3] = 0; // indicates the end of the expression + + // Position the final offsets + int endPos = data.length - 1; + + if (numArgs < 3) + { + // Set the offset to the false expression to be the current position + IntegerHelper.getTwoBytes(endPos - falseOffsetPos - 5, + data, falseOffsetPos); + } + + // Set the offset after the true expression + IntegerHelper.getTwoBytes(endPos - gotoEndPos - 2, + data, gotoEndPos); + + return data; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() + { + return 3; + } + + /** + * Default behaviour is to do nothing + * + * @param colAdjust the amount to add on to each relative cell reference + * @param rowAdjust the amount to add on to each relative row reference + */ + public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) + { + ParseItem[] operands = null; + + if (isIf()) + { + operands = ifConditions.getOperands(); + } + else + { + operands = getOperands(); + } + + for (int i = 0; i < operands.length; i++) + { + operands[i].adjustRelativeCellReferences(colAdjust, rowAdjust); + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param col the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnInserted(int sheetIndex, int col, boolean currentSheet) + { + ParseItem[] operands = null; + + if (isIf()) + { + operands = ifConditions.getOperands(); + } + else + { + operands = getOperands(); + } + + for (int i = 0; i < operands.length; i++) + { + operands[i].columnInserted(sheetIndex, col, currentSheet); + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param col the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnRemoved(int sheetIndex, int col, boolean currentSheet) + { + ParseItem[] operands = null; + + if (isIf()) + { + operands = ifConditions.getOperands(); + } + else + { + operands = getOperands(); + } + + for (int i = 0; i < operands.length; i++) + { + operands[i].columnRemoved(sheetIndex, col, currentSheet); + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was inserted + * @param row the row number which was inserted1 + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowInserted(int sheetIndex, int row, boolean currentSheet) + { + ParseItem[] operands = null; + + if (isIf()) + { + operands = ifConditions.getOperands(); + } + else + { + operands = getOperands(); + } + + for (int i = 0; i < operands.length; i++) + { + operands[i].rowInserted(sheetIndex, row, currentSheet); + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was removed + * @param row the row number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowRemoved(int sheetIndex, int row, boolean currentSheet) + { + ParseItem[] operands = null; + + if (isIf()) + { + operands = ifConditions.getOperands(); + } + else + { + operands = getOperands(); + } + + for (int i = 0; i < operands.length; i++) + { + operands[i].rowRemoved(sheetIndex, row, currentSheet); + } + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing, as operators don't have cell references + */ + void handleImportedCellReferences() + { + ParseItem[] operands = null; + + if (isIf()) + { + operands = ifConditions.getOperands(); + } + else + { + operands = getOperands(); + } + + for (int i = 0; i < operands.length; i++) + { + operands[i].handleImportedCellReferences(); + } + } +} + + + + Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/BinaryOperator.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/BinaryOperator.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/BinaryOperator.java 17 Aug 2012 14:51:16 -0000 1.1 @@ -0,0 +1,224 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +import common.Logger; + +import java.util.Stack; + +/** + * A cell reference in a formula + */ +abstract class BinaryOperator extends Operator implements ParsedThing +{ + // The logger + private static final Logger logger = Logger.getLogger(BinaryOperator.class); + + /** + * Constructor + */ + public BinaryOperator() + { + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) + { + return 0; + } + + /** + * Gets the operands for this operator from the stack + * + * @param s the token stack + */ + public void getOperands(Stack s) + { + ParseItem o1 = (ParseItem) s.pop(); + ParseItem o2 = (ParseItem) s.pop(); + + add(o1); + add(o2); + } + + /** + * Gets the string version of this binary operator + * + * @param buf a the string buffer + */ + public void getString(StringBuffer buf) + { + ParseItem[] operands = getOperands(); + operands[1].getString(buf); + buf.append(getSymbol()); + operands[0].getString(buf); + } + + /** + * Adjusts all the relative cell references in this formula by the + * amount specified. Used when copying formulas + * + * @param colAdjust the amount to add on to each relative cell reference + * @param rowAdjust the amount to add on to each relative row reference + */ + public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) + { + ParseItem[] operands = getOperands(); + operands[1].adjustRelativeCellReferences(colAdjust, rowAdjust); + operands[0].adjustRelativeCellReferences(colAdjust, rowAdjust); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param col the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnInserted(int sheetIndex, int col, boolean currentSheet) + { + ParseItem[] operands = getOperands(); + operands[1].columnInserted(sheetIndex, col, currentSheet); + operands[0].columnInserted(sheetIndex, col, currentSheet); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param col the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnRemoved(int sheetIndex, int col, boolean currentSheet) + { + ParseItem[] operands = getOperands(); + operands[1].columnRemoved(sheetIndex, col, currentSheet); + operands[0].columnRemoved(sheetIndex, col, currentSheet); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was inserted + * @param row the row number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowInserted(int sheetIndex, int row, boolean currentSheet) + { + ParseItem[] operands = getOperands(); + operands[1].rowInserted(sheetIndex, row, currentSheet); + operands[0].rowInserted(sheetIndex, row, currentSheet); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was removed + * @param row the row number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowRemoved(int sheetIndex, int row, boolean currentSheet) + { + ParseItem[] operands = getOperands(); + operands[1].rowRemoved(sheetIndex, row, currentSheet); + operands[0].rowRemoved(sheetIndex, row, currentSheet); + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() + { + // Get the data for the operands + ParseItem[] operands = getOperands(); + byte[] data = new byte[0]; + + // Get the operands in reverse order to get the RPN + for (int i = operands.length - 1; i >= 0; i--) + { + byte[] opdata = operands[i].getBytes(); + + // Grow the array + byte[] newdata = new byte[data.length + opdata.length]; + System.arraycopy(data, 0, newdata, 0, data.length); + System.arraycopy(opdata, 0, newdata, data.length, opdata.length); + data = newdata; + } + + // Add on the operator byte + byte[] newdata = new byte[data.length + 1]; + System.arraycopy(data, 0, newdata, 0, data.length); + newdata[data.length] = getToken().getCode(); + + return newdata; + } + + /** + * Abstract method which gets the binary operator string symbol + * + * @return the string symbol for this token + */ + abstract String getSymbol(); + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + abstract Token getToken(); + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing, as operators don't have cell references + */ + void handleImportedCellReferences() + { + ParseItem[] operands = getOperands(); + operands[0].handleImportedCellReferences(); + operands[1].handleImportedCellReferences(); + } + +} + + + + + Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/BooleanValue.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/BooleanValue.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/BooleanValue.java 17 Aug 2012 14:51:16 -0000 1.1 @@ -0,0 +1,97 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +/** + * A boolean operand in a formula + */ +class BooleanValue extends Operand implements ParsedThing +{ + /** + * The boolean value + */ + private boolean value; + + /** + * Constructor + */ + public BooleanValue() + { + } + + /** + * Constructor used when parsing a string formula + * + * @param s the string token, including quote marks + */ + public BooleanValue(String s) + { + // remove the quotes + value = Boolean.valueOf(s).booleanValue(); + } + + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) + { + value = data[pos] == 1 ? true : false; + return 1; + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() + { + byte[] data = new byte[2]; + data[0] = Token.BOOL.getCode(); + data[1] = (byte) (value == true ? 1 : 0); + + return data; + } + + /** + * Abstract method implementation to get the string equivalent of this + * token + * + * @param buf the string to append to + */ + public void getString(StringBuffer buf) + { + buf.append((new Boolean(value)).toString()); + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing + */ + void handleImportedCellReferences() + { + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/BuiltInFunction.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/BuiltInFunction.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/BuiltInFunction.java 17 Aug 2012 14:51:16 -0000 1.1 @@ -0,0 +1,289 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +import java.util.Stack; + +import common.Assert; +import common.Logger; + +import jxl.WorkbookSettings; +import jxl.biff.IntegerHelper; + +/** + * A built in function in a formula + */ +class BuiltInFunction extends Operator implements ParsedThing +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(BuiltInFunction.class); + + /** + * The function + */ + private Function function; + + /** + * The workbook settings + */ + private WorkbookSettings settings; + + /** + * Constructor + * @param ws the workbook settings + */ + public BuiltInFunction(WorkbookSettings ws) + { + settings = ws; + } + + /** + * Constructor used when parsing a formula from a string + * + * @param f the function + * @param ws the workbook settings + */ + public BuiltInFunction(Function f, WorkbookSettings ws) + { + function = f; + settings = ws; + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) + { + int index = IntegerHelper.getInt(data[pos], data[pos + 1]); + function = Function.getFunction(index); + Assert.verify(function != Function.UNKNOWN, "function code " + index); + return 2; + } + + /** + * Gets the operands for this operator from the stack + * + * @param s the token stack + */ + public void getOperands(Stack s) + { + // parameters are in the correct order, god damn them + ParseItem[] items = new ParseItem[function.getNumArgs()]; + // modified in 2.4.3 + for (int i = function.getNumArgs() - 1; i >= 0; i--) + { + ParseItem pi = (ParseItem) s.pop(); + + items[i] = pi; + } + + for (int i = 0; i < function.getNumArgs(); i++) + { + add(items[i]); + } + } + + /** + * Gets the string for this functions + * + * @param buf the buffer to append + */ + public void getString(StringBuffer buf) + { + buf.append(function.getName(settings)); + buf.append('('); + + int numArgs = function.getNumArgs(); + + if (numArgs > 0) + { + ParseItem[] operands = getOperands(); + + // arguments are in the same order they were specified + operands[0].getString(buf); + + for (int i = 1; i < numArgs; i++) + { + buf.append(','); + operands[i].getString(buf); + } + } + + buf.append(')'); + } + + /** + * Adjusts all the relative cell references in this formula by the + * amount specified. Used when copying formulas + * + * @param colAdjust the amount to add on to each relative cell reference + * @param rowAdjust the amount to add on to each relative row reference + */ + public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) + { + ParseItem[] operands = getOperands(); + + for (int i = 0; i < operands.length; i++) + { + operands[i].adjustRelativeCellReferences(colAdjust, rowAdjust); + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param col the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnInserted(int sheetIndex, int col, boolean currentSheet) + { + ParseItem[] operands = getOperands(); + for (int i = 0; i < operands.length; i++) + { + operands[i].columnInserted(sheetIndex, col, currentSheet); + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param col the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnRemoved(int sheetIndex, int col, boolean currentSheet) + { + ParseItem[] operands = getOperands(); + for (int i = 0; i < operands.length; i++) + { + operands[i].columnRemoved(sheetIndex, col, currentSheet); + } + } + + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was inserted + * @param row the row number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowInserted(int sheetIndex, int row, boolean currentSheet) + { + ParseItem[] operands = getOperands(); + for (int i = 0; i < operands.length; i++) + { + operands[i].rowInserted(sheetIndex, row, currentSheet); + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was removed + * @param row the row number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowRemoved(int sheetIndex, int row, boolean currentSheet) + { + ParseItem[] operands = getOperands(); + for (int i = 0; i < operands.length; i++) + { + operands[i].rowRemoved(sheetIndex, row, currentSheet); + } + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing, as operators don't have cell references + */ + void handleImportedCellReferences() + { + ParseItem[] operands = getOperands(); + for (int i = 0 ; i < operands.length ; i++) + { + operands[i].handleImportedCellReferences(); + } + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() + { + // Get the data for the operands + ParseItem[] operands = getOperands(); + byte[] data = new byte[0]; + + for (int i = 0; i < operands.length; i++) + { + byte[] opdata = operands[i].getBytes(); + + // Grow the array + byte[] newdata = new byte[data.length + opdata.length]; + System.arraycopy(data, 0, newdata, 0, data.length); + System.arraycopy(opdata, 0, newdata, data.length, opdata.length); + data = newdata; + } + + // Add on the operator byte + byte[] newdata = new byte[data.length + 3]; + System.arraycopy(data, 0, newdata, 0, data.length); + newdata[data.length] = !useAlternateCode() ? Token.FUNCTION.getCode() : + Token.FUNCTION.getCode2(); + IntegerHelper.getTwoBytes(function.getCode(), newdata, data.length + 1); + + return newdata; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() + { + return 3; + } +} + Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/CellReference.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/CellReference.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/CellReference.java 17 Aug 2012 14:51:16 -0000 1.1 @@ -0,0 +1,296 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +import common.Logger; + +import jxl.Cell; +import jxl.biff.CellReferenceHelper; +import jxl.biff.IntegerHelper; + +/** + * A cell reference in a formula + */ +class CellReference extends Operand implements ParsedThing +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(CellReference.class); + + /** + * Indicates whether the column reference is relative or absolute + */ + private boolean columnRelative; + + /** + * Indicates whether the row reference is relative or absolute + */ + private boolean rowRelative; + + /** + * The column reference + */ + private int column; + + /** + * The row reference + */ + private int row; + + /** + * The cell containing the formula. Stored in order to determine + * relative cell values + */ + private Cell relativeTo; + + /** + * Constructor + * + * @param rt the cell containing the formula + */ + public CellReference(Cell rt) + { + relativeTo = rt; + } + + /** + * Constructor + */ + public CellReference() + { + } + + /** + * Constructor invoked when parsing a text string + * + * @param s the string being parsed + */ + public CellReference(String s) + { + column = CellReferenceHelper.getColumn(s); + row = CellReferenceHelper.getRow(s); + columnRelative = CellReferenceHelper.isColumnRelative(s); + rowRelative = CellReferenceHelper.isRowRelative(s); + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) + { + row = IntegerHelper.getInt(data[pos], data[pos + 1]); + int columnMask = IntegerHelper.getInt(data[pos + 2], data[pos + 3]); + column = columnMask & 0x00ff; + columnRelative = ((columnMask & 0x4000) != 0); + rowRelative = ((columnMask & 0x8000) != 0); + + return 4; + } + + /** + * Accessor for the column + * + * @return the column + */ + public int getColumn() + { + return column; + } + + /** + * Accessor for the row + * + * @return the row + */ + public int getRow() + { + return row; + } + + /** + * Gets the cell reference as a string for this item + * + * @param buf the string buffer to populate + */ + public void getString(StringBuffer buf) + { + CellReferenceHelper.getCellReference(column, !columnRelative, + row, !rowRelative, + buf); + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() + { + byte[] data = new byte[5]; + data[0] = !useAlternateCode() ? Token.REF.getCode() : + Token.REF.getCode2(); + + IntegerHelper.getTwoBytes(row, data, 1); + + int grcol = column; + + // Set the row/column relative bits if applicable + if (rowRelative) + { + grcol |= 0x8000; + } + + if (columnRelative) + { + grcol |= 0x4000; + } + + IntegerHelper.getTwoBytes(grcol, data, 3); + + return data; + } + + /** + * Adjusts all the relative cell references in this formula by the + * amount specified. Used when copying formulas + * + * @param colAdjust the amount to add on to each relative cell reference + * @param rowAdjust the amount to add on to each relative row reference + */ + public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) + { + if (columnRelative) + { + column += colAdjust; + } + + if (rowRelative) + { + row += rowAdjust; + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param col the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + public void columnInserted(int sheetIndex, int col, boolean currentSheet) + { + if (!currentSheet) + { + return; + } + + if (column >= col) + { + column++; + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param col the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnRemoved(int sheetIndex, int col, boolean currentSheet) + { + if (!currentSheet) + { + return; + } + + if (column >= col) + { + column--; + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was inserted + * @param r the row number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowInserted(int sheetIndex, int r, boolean currentSheet) + { + if (!currentSheet) + { + return; + } + + if (row >= r) + { + row++; + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was removed + * @param r the row number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowRemoved(int sheetIndex, int r, boolean currentSheet) + { + if (!currentSheet) + { + return; + } + + if (row >= r) + { + row--; + } + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Flags the formula as invalid + * Does nothing here + */ + void handleImportedCellReferences() + { + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/CellReference3d.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/CellReference3d.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/CellReference3d.java 17 Aug 2012 14:51:16 -0000 1.1 @@ -0,0 +1,329 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +import common.Logger; + +import jxl.Cell; + +import jxl.biff.CellReferenceHelper; +import jxl.biff.IntegerHelper; + +/** + * A 3d cell reference in a formula + */ +class CellReference3d extends Operand implements ParsedThing +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(CellReference3d.class); + + /** + * Indicates whether the column reference is relative or absolute + */ + private boolean columnRelative; + + /** + * Indicates whether the row reference is relative or absolute + */ + private boolean rowRelative; + + /** + * The column reference + */ + private int column; + + /** + * The row reference + */ + private int row; + + /** + * The cell containing the formula. Stored in order to determine + * relative cell values + */ + private Cell relativeTo; + + /** + * The sheet which the reference is present on + */ + private int sheet; + + /** + * A handle to the container of the external sheets ie. the workbook + */ + private ExternalSheet workbook; + + /** + * Constructor + * + * @param rt the cell containing the formula + * @param w the list of external sheets + */ + public CellReference3d(Cell rt, ExternalSheet w) + { + relativeTo = rt; + workbook = w; + } + + /** + * Constructs this object from a string + * + * @param s the string + * @param w the external sheet + * @exception FormulaException + */ + public CellReference3d(String s, ExternalSheet w) throws FormulaException + { + workbook = w; + columnRelative = true; + rowRelative = true; + + // Get the cell details + int sep = s.indexOf('!'); + String cellString = s.substring(sep + 1); + column = CellReferenceHelper.getColumn(cellString); + row = CellReferenceHelper.getRow(cellString); + + // Get the sheet index + String sheetName = s.substring(0, sep); + + // Remove single quotes, if they exist + if (sheetName.charAt(0) == '\'' && + sheetName.charAt(sheetName.length() - 1) == '\'') + { + sheetName = sheetName.substring(1, sheetName.length() - 1); + } + sheet = w.getExternalSheetIndex(sheetName); + + if (sheet < 0) + { + throw new FormulaException(FormulaException.SHEET_REF_NOT_FOUND, + sheetName); + } + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) + { + sheet = IntegerHelper.getInt(data[pos], data[pos + 1]); + row = IntegerHelper.getInt(data[pos + 2], data[pos + 3]); + int columnMask = IntegerHelper.getInt(data[pos + 4], data[pos + 5]); + column = columnMask & 0x00ff; + columnRelative = ((columnMask & 0x4000) != 0); + rowRelative = ((columnMask & 0x8000) != 0); + + return 6; + } + + /** + * Accessor for the column + * + * @return the column number + */ + public int getColumn() + { + return column; + } + + /** + * Accessor for the row + * + * @return the row number + */ + public int getRow() + { + return row; + } + + /** + * Gets the string version of this cell reference + * + * @param buf the buffer to append to + */ + public void getString(StringBuffer buf) + { + CellReferenceHelper.getCellReference(sheet, column, !columnRelative, + row, !rowRelative, + workbook, buf); + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() + { + byte[] data = new byte[7]; + data[0] = Token.REF3D.getCode(); + + IntegerHelper.getTwoBytes(sheet, data, 1); + IntegerHelper.getTwoBytes(row, data, 3); + + int grcol = column; + + // Set the row/column relative bits if applicable + if (rowRelative) + { + grcol |= 0x8000; + } + + if (columnRelative) + { + grcol |= 0x4000; + } + + IntegerHelper.getTwoBytes(grcol, data, 5); + + return data; + } + + /** + * Adjusts all the relative cell references in this formula by the + * amount specified. Used when copying formulas + * + * @param colAdjust the amount to add on to each relative cell reference + * @param rowAdjust the amount to add on to each relative row reference + */ + public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) + { + if (columnRelative) + { + column += colAdjust; + } + + if (rowRelative) + { + row += rowAdjust; + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param col the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + public void columnInserted(int sheetIndex, int col, boolean currentSheet) + { + if (sheetIndex != sheet) + { + return; + } + + if (column >= col) + { + column++; + } + } + + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param col the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnRemoved(int sheetIndex, int col, boolean currentSheet) + { + if (sheetIndex != sheet) + { + return; + } + + if (column >= col) + { + column--; + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was inserted + * @param r the row number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowInserted(int sheetIndex, int r, boolean currentSheet) + { + if (sheetIndex != sheet) + { + return; + } + + if (row >= r) + { + row++; + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was removed + * @param r the row number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowRemoved(int sheetIndex, int r, boolean currentSheet) + { + if (sheetIndex != sheet) + { + return; + } + + if (row >= r) + { + row--; + } + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Flags the formula as invalid + */ + void handleImportedCellReferences() + { + setInvalid(); + } + +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/CellReferenceError.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/CellReferenceError.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/CellReferenceError.java 17 Aug 2012 14:51:15 -0000 1.1 @@ -0,0 +1,88 @@ +/********************************************************************* +* +* Copyright (C) 2005 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +import common.Logger; + +/** + * An cell reference error which occurs in a formula + */ +class CellReferenceError extends Operand implements ParsedThing +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(CellReferenceError.class); + + /** + * Constructor + */ + public CellReferenceError() + { + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) + { + // the data is unused - just return the four bytes + + return 4; + } + + /** + * Gets the cell reference as a string for this item + * + * @param buf the string buffer to populate + */ + public void getString(StringBuffer buf) + { + buf.append(FormulaErrorCode.REF.getDescription()); + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() + { + byte[] data = new byte[5]; + data[0] = Token.REFERR.getCode(); + + // bytes 1-5 are unused + + return data; + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing + */ + void handleImportedCellReferences() + { + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/CloseParentheses.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/CloseParentheses.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/CloseParentheses.java 17 Aug 2012 14:51:16 -0000 1.1 @@ -0,0 +1,33 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +/** + * The close of parentheses token + */ +class CloseParentheses extends StringParseItem +{ + /** + * Constructor + */ + public CloseParentheses() + { + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/ColumnRange.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/ColumnRange.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/ColumnRange.java 17 Aug 2012 14:51:15 -0000 1.1 @@ -0,0 +1,85 @@ +/********************************************************************* +* +* Copyright (C) 2005 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +import common.Assert; +import common.Logger; + +import jxl.biff.CellReferenceHelper; + +/** + * A class to hold range information across two entire columns + */ +class ColumnRange extends Area +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(ColumnRange.class); + + /** + * Constructor + */ + ColumnRange() + { + super(); + } + + /** + * Constructor invoked when parsing a string formula + * + * @param s the string to parse + */ + ColumnRange(String s) + { + int seppos = s.indexOf(":"); + Assert.verify(seppos != -1); + String startcell = s.substring(0, seppos); + String endcell = s.substring(seppos + 1); + + int columnFirst = CellReferenceHelper.getColumn(startcell); + int rowFirst = 0; + int columnLast = CellReferenceHelper.getColumn(endcell); + int rowLast = 0xffff; + + boolean columnFirstRelative = + CellReferenceHelper.isColumnRelative(startcell); + boolean rowFirstRelative = false; + boolean columnLastRelative = CellReferenceHelper.isColumnRelative(endcell); + boolean rowLastRelative = false; + + setRangeData(columnFirst, columnLast, + rowFirst, rowLast, + columnFirstRelative, columnLastRelative, + rowFirstRelative, rowLastRelative); + } + + /** + * Gets the string representation of this item + * + * @param buf the string buffer + */ + public void getString(StringBuffer buf) + { + CellReferenceHelper.getColumnReference(getFirstColumn(), buf); + buf.append(':'); + CellReferenceHelper.getColumnReference(getLastColumn(), buf); + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/ColumnRange3d.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/ColumnRange3d.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/ColumnRange3d.java 17 Aug 2012 14:51:15 -0000 1.1 @@ -0,0 +1,129 @@ +/********************************************************************* +* +* Copyright (C) 2005 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +import common.Assert; +import common.Logger; + +import jxl.biff.CellReferenceHelper; + +/** + * A nested class to hold range information + */ +class ColumnRange3d extends Area3d +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(ColumnRange3d.class); + + /** + * A handle to the workbook + */ + private ExternalSheet workbook; + + /** + * The sheet number + */ + private int sheet; + + /** + * Constructor + * + * @param es the external sheet + */ + ColumnRange3d(ExternalSheet es) + { + super(es); + workbook = es; + } + + /** + * Constructor invoked when parsing a string formula + * + * @param s the string to parse + * @param es the external sheet + * @exception FormulaException + */ + ColumnRange3d(String s, ExternalSheet es) throws FormulaException + { + super(es); + workbook = es; + int seppos = s.lastIndexOf(":"); + Assert.verify(seppos != -1); + String startcell = s.substring(0, seppos); + String endcell = s.substring(seppos + 1); + + // Get the the start cell details + int sep = s.indexOf('!'); + String cellString = s.substring(sep + 1, seppos); + int columnFirst = CellReferenceHelper.getColumn(cellString); + int rowFirst = 0; + + // Get the sheet index + String sheetName = s.substring(0, sep); + int sheetNamePos = sheetName.lastIndexOf(']'); + + // Remove single quotes, if they exist + if (sheetName.charAt(0) == '\'' && + sheetName.charAt(sheetName.length() - 1) == '\'') + { + sheetName = sheetName.substring(1, sheetName.length() - 1); + } + + sheet = es.getExternalSheetIndex(sheetName); + + if (sheet < 0) + { + throw new FormulaException(FormulaException.SHEET_REF_NOT_FOUND, + sheetName); + } + + // Get the last cell index + int columnLast = CellReferenceHelper.getColumn(endcell); + int rowLast = 0xffff; + + boolean columnFirstRelative = true; + boolean rowFirstRelative = true; + boolean columnLastRelative = true; + boolean rowLastRelative = true; + + setRangeData(sheet, columnFirst, columnLast, rowFirst, rowLast, + columnFirstRelative, rowFirstRelative, + columnLastRelative, rowLastRelative); + } + + /** + * Gets the string representation of this column range + * + * @param buf the string buffer to append to + */ + public void getString(StringBuffer buf) + { + buf.append('\''); + buf.append(workbook.getExternalSheetName(sheet)); + buf.append('\''); + buf.append('!'); + + CellReferenceHelper.getColumnReference(getFirstColumn(), buf); + buf.append(':'); + CellReferenceHelper.getColumnReference(getLastColumn(), buf); + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/Concatenate.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/Concatenate.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/Concatenate.java 17 Aug 2012 14:51:15 -0000 1.1 @@ -0,0 +1,64 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +/** + * A cell reference in a formula + */ +class Concatenate extends BinaryOperator implements ParsedThing +{ + /** + * Constructor + */ + public Concatenate() + { + } + + /** + * Gets the string representation of this symbol + * + * @return the concatenation symbol + */ + public String getSymbol() + { + return "&"; + } + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + Token getToken() + { + return Token.CONCAT; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() + { + return 3; + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/Divide.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/Divide.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/Divide.java 17 Aug 2012 14:51:16 -0000 1.1 @@ -0,0 +1,65 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +/** + * A cell reference in a formula + */ +class Divide extends BinaryOperator implements ParsedThing +{ + /** + * Constructor + */ + public Divide() + { + } + + /** + * Gets the string representation of this symbol + * + * @return the symbol + */ + public String getSymbol() + { + return "/"; + } + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + Token getToken() + { + return Token.DIVIDE; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() + { + return 3; + } + +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/DoubleValue.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/DoubleValue.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/DoubleValue.java 17 Aug 2012 14:51:15 -0000 1.1 @@ -0,0 +1,124 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +import common.Logger; + +import jxl.biff.DoubleHelper; + +/** + * A cell reference in a formula + */ +class DoubleValue extends NumberValue implements ParsedThing +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(DoubleValue.class); + + /** + * The value of this double in the formula + */ + private double value; + + /** + * Constructor + */ + public DoubleValue() + { + } + + /** + * Constructor - invoked when writing an integer value that's out + * of range for a short + * + * @param v the double value + */ + DoubleValue(double v) + { + value = v; + } + + /** + * Constructor for a double value when reading from a string + * + * @param s the string representation of this token + */ + public DoubleValue(String s) + { + try + { + value = Double.parseDouble(s); + } + catch (NumberFormatException e) + { + logger.warn(e, e); + value = 0; + } + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) + { + value = DoubleHelper.getIEEEDouble(data, pos); + + return 8; + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() + { + byte[] data = new byte[9]; + data[0] = Token.DOUBLE.getCode(); + + DoubleHelper.getIEEEBytes(value, data, 1); + + return data; + } + + /** + * The double value + * + * @return the value + */ + public double getValue() + { + return value; + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing + */ + void handleImportedCellReferences() + { + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/Equal.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/Equal.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/Equal.java 17 Aug 2012 14:51:17 -0000 1.1 @@ -0,0 +1,66 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +/** + * A cell reference in a formula + */ +class Equal extends BinaryOperator implements ParsedThing +{ + /** + * Constructor + */ + public Equal() + { + } + + /** + * Gets the string for this symbol + * + * @return the symbol string + */ + public String getSymbol() + { + return "="; + } + + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + Token getToken() + { + return Token.EQUAL; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() + { + return 5; + } + +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/ErrorConstant.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/ErrorConstant.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/ErrorConstant.java 17 Aug 2012 14:51:17 -0000 1.1 @@ -0,0 +1,98 @@ +/********************************************************************* +* +* Copyright (C) 2006 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +import jxl.biff.IntegerHelper; + +/** + * An error constant + */ +class ErrorConstant extends Operand implements ParsedThing +{ + /** + * The error + */ + private FormulaErrorCode error; + + /** + * Constructor + */ + public ErrorConstant() + { + } + + /** + * Constructor + * + * @param s the error constant + */ + public ErrorConstant(String s) + { + error = FormulaErrorCode.getErrorCode(s); + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) + { + int code = data[pos]; + error = FormulaErrorCode.getErrorCode(code); + return 1; + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() + { + byte[] data = new byte[2]; + data[0] = Token.ERR.getCode(); + data[1] = (byte) error.getCode(); + + return data; + } + + /** + * Abstract method implementation to get the string equivalent of this + * token + * + * @param buf the string to append to + */ + public void getString(StringBuffer buf) + { + buf.append(error.getDescription()); + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing + */ + void handleImportedCellReferences() + { + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/ExternalSheet.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/ExternalSheet.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/ExternalSheet.java 17 Aug 2012 14:51:17 -0000 1.1 @@ -0,0 +1,77 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +import jxl.read.biff.BOFRecord; + +/** + * Interface which exposes the methods needed by formulas + * to access external sheet records + */ +public interface ExternalSheet +{ + /** + * Gets the name of the external sheet specified by the index + * + * @param index the external sheet index + * @return the name of the external sheet + */ + public String getExternalSheetName(int index); + + /** + * Gets the index of the first external sheet for the name + * + * @param sheetName the name of the external sheet + * @return the index of the external sheet with the specified name + */ + public int getExternalSheetIndex(String sheetName); + + /** + * Gets the index of the first external sheet for the name + * + * @param index the external sheet index + * @return the sheet index of the external sheet index + */ + public int getExternalSheetIndex(int index); + + /** + * Gets the index of the last external sheet for the name + * + * @param sheetName the name of the external sheet + * @return the index of the external sheet with the specified name + */ + public int getLastExternalSheetIndex(String sheetName); + + /** + * Gets the index of the first external sheet for the name + * + * @param index the external sheet index + * @return the sheet index of the external sheet index + */ + public int getLastExternalSheetIndex(int index); + + /** + * Parsing of formulas is only supported for a subset of the available + * biff version, so we need to test to see if this version is acceptable + * + * @return the BOF record + */ + public BOFRecord getWorkbookBof(); +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/FormulaErrorCode.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/FormulaErrorCode.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/FormulaErrorCode.java 17 Aug 2012 14:51:16 -0000 1.1 @@ -0,0 +1,142 @@ +/********************************************************************* +* +* Copyright (C) 2005 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +/** + * Enumeration for formula error codes + */ +public class FormulaErrorCode +{ + /** + * The error code + */ + private int errorCode; + + /** + * The description + */ + private String description; + + /** + * The list of error codes + */ + private static FormulaErrorCode[] codes = new FormulaErrorCode[0]; + + /** + * Constructor + * + * @param code the code + * @param desc the description + */ + FormulaErrorCode(int code, String desc) + { + errorCode = code; + description = desc; + FormulaErrorCode[] newcodes = new FormulaErrorCode[codes.length + 1]; + System.arraycopy(codes, 0, newcodes, 0, codes.length); + newcodes[codes.length] = this; + codes = newcodes; + } + + /** + * Accessor for the code + * + * @return the code + */ + public int getCode() + { + return errorCode; + } + + /** + * Accessor for the description + * + * @return the description + */ + public String getDescription() + { + return description; + } + + /** + * Gets the error type given just the code + * + * @param code the code to lookup + * @return the error type + */ + public static FormulaErrorCode getErrorCode(int code) + { + boolean found = false; + FormulaErrorCode ec = UNKNOWN; + for (int i = 0; i < codes.length && !found; i++) + { + if (codes[i].errorCode == code) + { + found = true; + ec = codes[i]; + } + } + return ec; + } + + /** + * Gets the error type given the string value + * + * @param code the code to lookup + * @return the error type + */ + public static FormulaErrorCode getErrorCode(String code) + { + boolean found = false; + FormulaErrorCode ec = UNKNOWN; + + if (code == null || code.length() == 0) + { + return ec; + } + + for (int i = 0; i < codes.length && !found; i++) + { + if (codes[i].description.equals(code)) + { + found = true; + ec = codes[i]; + } + } + return ec; + } + + public static final FormulaErrorCode UNKNOWN = + new FormulaErrorCode(0xff, "?"); + public static final FormulaErrorCode NULL = + new FormulaErrorCode(0x0, "#NULL!"); + public static final FormulaErrorCode DIV0 = + new FormulaErrorCode(0x7, "#DIV/0!"); + public static final FormulaErrorCode VALUE = + new FormulaErrorCode(0xf, "#VALUE!"); + public static final FormulaErrorCode REF = + new FormulaErrorCode(0x17, "#REF!"); + public static final FormulaErrorCode NAME = + new FormulaErrorCode(0x1d, "#NAME?"); + public static final FormulaErrorCode NUM = new FormulaErrorCode(0x24, + "#NUM!"); + public static final FormulaErrorCode NA = new FormulaErrorCode(0x2a, + "#N/A!"); +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/FormulaException.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/FormulaException.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/FormulaException.java 17 Aug 2012 14:51:17 -0000 1.1 @@ -0,0 +1,127 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +import jxl.JXLException; + +/** + * Exception thrown when parsing a formula + */ +public class FormulaException extends JXLException +{ + /** + * Inner class containing the message + */ + private static class FormulaMessage + { + /** + * The message + */ + private String message; + + /** + * Constructs this exception with the specified message + * + * @param m the message + */ + FormulaMessage(String m) + { + message = m; + } + + /** + * Accessor for the message + * + * @return the message + */ + public String getMessage() + { + return message; + } + } + + /** + */ + static final FormulaMessage UNRECOGNIZED_TOKEN = + new FormulaMessage("Unrecognized token"); + + /** + */ + static final FormulaMessage UNRECOGNIZED_FUNCTION = + new FormulaMessage("Unrecognized function"); + + /** + */ + public static final FormulaMessage BIFF8_SUPPORTED = + new FormulaMessage("Only biff8 formulas are supported"); + + /** + */ + static final FormulaMessage LEXICAL_ERROR = + new FormulaMessage("Lexical error: "); + + /** + */ + static final FormulaMessage INCORRECT_ARGUMENTS = + new FormulaMessage("Incorrect arguments supplied to function"); + + /** + */ + static final FormulaMessage SHEET_REF_NOT_FOUND = + new FormulaMessage("Could not find sheet"); + + /** + */ + static final FormulaMessage CELL_NAME_NOT_FOUND = + new FormulaMessage("Could not find named cell"); + + + /** + * Constructs this exception with the specified message + * + * @param m the message + */ + public FormulaException(FormulaMessage m) + { + super(m.message); + } + + /** + * Constructs this exception with the specified message + * + * @param m the message + * @param val the value + */ + public FormulaException(FormulaMessage m, int val) + { + super(m.message + " " + val); + } + + /** + * Constructs this exception with the specified message + * + * @param m the message + * @param val the value + */ + public FormulaException(FormulaMessage m, String val) + { + super(m.message + " " + val); + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/FormulaParser.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/FormulaParser.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/FormulaParser.java 17 Aug 2012 14:51:15 -0000 1.1 @@ -0,0 +1,205 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +import common.Assert; +import common.Logger; + +import jxl.Cell; +import jxl.WorkbookSettings; +import jxl.biff.WorkbookMethods; + +/** + * Parses the formula passed in (either as parsed strings or as a string) + * into a tree of operators and operands + */ +public class FormulaParser +{ + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(FormulaParser.class); + + /** + * The formula parser. The object implementing this interface will either + * parse tokens or strings + */ + private Parser parser; + + /** + * Constructor which creates the parse tree out of tokens + * + * @param tokens the list of parsed tokens + * @param rt the cell containing the formula + * @param es a handle to the external sheet + * @param nt a handle to the name table + * @param ws the workbook settings + * @exception FormulaException + */ + public FormulaParser(byte[] tokens, + Cell rt, + ExternalSheet es, + WorkbookMethods nt, + WorkbookSettings ws) + throws FormulaException + { + // A null workbook bof means that it is a writable workbook and therefore + // must be biff8 + if (es.getWorkbookBof() != null && + !es.getWorkbookBof().isBiff8()) + { + throw new FormulaException(FormulaException.BIFF8_SUPPORTED); + } + Assert.verify(nt != null); + parser = new TokenFormulaParser(tokens, rt, es, nt, ws); + } + + /** + * Constructor which creates the parse tree out of the string + * + * @param form the formula string + * @param es the external sheet handle + * @param nt the name table + * @param ws the workbook settings + */ + public FormulaParser(String form, + ExternalSheet es, + WorkbookMethods nt, + WorkbookSettings ws) + { + parser = new StringFormulaParser(form, es, nt, ws); + } + + + /** + * Adjusts all the relative cell references in this formula by the + * amount specified. Used when copying formulas + * + * @param colAdjust the amount to add on to each relative cell reference + * @param rowAdjust the amount to add on to each relative row reference + */ + public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) + { + parser.adjustRelativeCellReferences(colAdjust, rowAdjust); + } + + /** + * Parses the formula into a parse tree + * + * @exception FormulaException + */ + public void parse() throws FormulaException + { + parser.parse(); + } + + /** + * Gets the formula as a string + * + * @return the formula as a string + * @exception FormulaException + */ + public String getFormula() throws FormulaException + { + return parser.getFormula(); + } + + /** + * Gets the bytes for the formula. This takes into account any + * token mapping necessary because of shared formulas + * + * @return the bytes in RPN + */ + public byte[] getBytes() + { + return parser.getBytes(); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param col the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + public void columnInserted(int sheetIndex, int col, boolean currentSheet) + { + parser.columnInserted(sheetIndex, col, currentSheet); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param col the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + public void columnRemoved(int sheetIndex, int col, boolean currentSheet) + { + parser.columnRemoved(sheetIndex, col, currentSheet); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param row the row number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + public void rowInserted(int sheetIndex, int row, boolean currentSheet) + { + parser.rowInserted(sheetIndex, row, currentSheet); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param row the row number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + public void rowRemoved(int sheetIndex, int row, boolean currentSheet) + { + parser.rowRemoved(sheetIndex, row, currentSheet); + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * + * @return TRUE if the formula is valid import, FALSE otherwise + */ + public boolean handleImportedCellReferences() + { + return parser.handleImportedCellReferences(); + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/Function.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/Function.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/Function.java 17 Aug 2012 14:51:16 -0000 1.1 @@ -0,0 +1,716 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +import common.Logger; + +import jxl.WorkbookSettings; + +/** + * An enumeration detailing the Excel function codes + */ +final class Function +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(Function.class); + + /** + * The code which applies to this function + */ + private final int code; + + /** + * The property name of this function + */ + private final String name; + + /** + * The number of args this function expects + */ + private final int numArgs; + + + /** + * All available functions. This attribute is package protected in order + * to enable the FunctionNames to initialize + */ + private static Function[] functions = new Function[0]; + + /** + * Constructor + * Sets the token value and adds this token to the array of all token + * + * @param v the biff code for the token + * @param s the string + * @param a the number of arguments + */ + private Function(int v, String s, int a) + { + code = v; + name = s; + numArgs = a; + + // Grow the array + Function[] newarray = new Function[functions.length + 1]; + System.arraycopy(functions, 0, newarray, 0, functions.length); + newarray[functions.length] = this; + functions = newarray; + } + + /** + * Standard hash code method + * + * @return the hash code + */ + public int hashCode() + { + return code; + } + + /** + * Gets the function code - used when generating token data + * + * @return the code + */ + int getCode() + { + return code; + } + + /** + * Gets the property name. Used by the FunctionNames object when initializing + * the locale specific names + * + * @return the property name for this function + */ + String getPropertyName() + { + return name; + } + + /** + * Gets the function name + * @param ws the workbook settings + * @return the function name + */ + String getName(WorkbookSettings ws) + { + FunctionNames fn = ws.getFunctionNames(); + return fn.getName(this); + } + + /** + * Gets the number of arguments for this function + * + * @return the number of arguments + */ + int getNumArgs() + { + return numArgs; + } + + /** + * Gets the type object from its integer value + * + * @param v the function value + * @return the function + */ + public static Function getFunction(int v) + { + Function f = null; + + for (int i = 0; i < functions.length; i++) + { + if (functions[i].code == v) + { + f = functions[i]; + break; + } + } + + return f != null ? f : UNKNOWN; + } + + /** + * Gets the type object from its string value. Used when parsing strings + * + * @param v the function name + * @param ws the workbook settings + * @return the function + */ + public static Function getFunction(String v, WorkbookSettings ws) + { + FunctionNames fn = ws.getFunctionNames(); + Function f = fn.getFunction(v); + return f != null ? f : UNKNOWN; + } + + /** + * Accessor for all the functions, used by the internationalization + * work around + * + * @return all the functions + */ + static Function[] getFunctions() + { + return functions; + } + + // The functions + + public static final Function COUNT = + new Function(0x0, "count", 0xff); + public static final Function ATTRIBUTE = new Function(0x1, "", 0xff); + public static final Function ISNA = + new Function(0x2, "isna", 1); + public static final Function ISERROR = + new Function(0x3, "iserror", 1); + public static final Function SUM = + new Function(0x4, "sum", 0xff); + public static final Function AVERAGE = + new Function(0x5, "average", 0xff); + public static final Function MIN = + new Function(0x6, "min", 0xff); + public static final Function MAX = + new Function(0x7, "max", 0xff); + public static final Function ROW = + new Function(0x8, "row", 0xff); + public static final Function COLUMN = + new Function(0x9, "column", 0xff); + public static final Function NA = + new Function(0xa, "na", 0); + public static final Function NPV = + new Function(0xb, "npv", 0xff); + public static final Function STDEV = + new Function(0xc, "stdev", 0xff); + public static final Function DOLLAR = + new Function(0xd, "dollar", 2); + public static final Function FIXED = + new Function(0xe, "fixed", 0xff); + public static final Function SIN = + new Function(0xf, "sin", 1); + public static final Function COS = + new Function(0x10, "cos", 1); + public static final Function TAN = + new Function(0x11, "tan", 1); + public static final Function ATAN = + new Function(0x12, "atan", 1); + public static final Function PI = + new Function(0x13, "pi", 0); + public static final Function SQRT = + new Function(0x14, "sqrt", 1); + public static final Function EXP = + new Function(0x15, "exp", 1); + public static final Function LN = + new Function(0x16, "ln", 1); + public static final Function LOG10 = + new Function(0x17, "log10", 1); + public static final Function ABS = + new Function(0x18, "abs", 1); + public static final Function INT = + new Function(0x19, "int", 1); + public static final Function SIGN = + new Function(0x1a, "sign", 1); + public static final Function ROUND = + new Function(0x1b, "round", 2); + public static final Function LOOKUP = + new Function(0x1c, "lookup", 2); + public static final Function INDEX = + new Function(0x1d, "index", 3); + public static final Function REPT = new Function(0x1e, "rept", 2); + public static final Function MID = + new Function(0x1f, "mid", 3); + public static final Function LEN = + new Function(0x20, "len", 1); + public static final Function VALUE = + new Function(0x21, "value", 1); + public static final Function TRUE = + new Function(0x22, "true", 0); + public static final Function FALSE = + new Function(0x23, "false", 0); + public static final Function AND = + new Function(0x24, "and", 0xff); + public static final Function OR = + new Function(0x25, "or", 0xff); + public static final Function NOT = + new Function(0x26, "not", 1); + public static final Function MOD = + new Function(0x27, "mod", 2); + public static final Function DCOUNT = + new Function(0x28, "dcount", 3); + public static final Function DSUM = + new Function(0x29, "dsum", 3); + public static final Function DAVERAGE = + new Function(0x2a, "daverage", 3); + public static final Function DMIN = + new Function(0x2b, "dmin", 3); + public static final Function DMAX = + new Function(0x2c, "dmax", 3); + public static final Function DSTDEV = + new Function(0x2d, "dstdev", 3); + public static final Function VAR = + new Function(0x2e, "var", 0xff); + public static final Function DVAR = + new Function(0x2f, "dvar", 3); + public static final Function TEXT = + new Function(0x30, "text", 2); + public static final Function LINEST = + new Function(0x31, "linest", 0xff); + public static final Function TREND = + new Function(0x32, "trend", 0xff); + public static final Function LOGEST = + new Function(0x33, "logest", 0xff); + public static final Function GROWTH = + new Function(0x34, "growth", 0xff); + //public static final Function GOTO = new Function(0x35, "GOTO",); + //public static final Function HALT = new Function(0x36, "HALT",); + public static final Function PV = + new Function(0x38, "pv", 0xff); + public static final Function FV = + new Function(0x39, "fv", 0xff); + public static final Function NPER = + new Function(0x3a, "nper", 0xff); + public static final Function PMT = + new Function(0x3b, "pmt", 0xff); + public static final Function RATE = + new Function(0x3c, "rate", 0xff); + //public static final Function MIRR = new Function(0x3d, "MIRR",); + //public static final Function IRR = new Function(0x3e, "IRR",); + public static final Function RAND = + new Function(0x3f, "rand", 0); + public static final Function MATCH = + new Function(0x40, "match", 3); + public static final Function DATE = + new Function(0x41, "date", 3); + public static final Function TIME = + new Function(0x42, "time", 3); + public static final Function DAY = + new Function(0x43, "day", 1); + public static final Function MONTH = + new Function(0x44, "month", 1); + public static final Function YEAR = + new Function(0x45, "year", 1); + public static final Function WEEKDAY = + new Function(0x46, "weekday", 2); + public static final Function HOUR = + new Function(0x47, "hour", 1); + public static final Function MINUTE = + new Function(0x48, "minute", 1); + public static final Function SECOND = + new Function(0x49, "second", 1); + public static final Function NOW = + new Function(0x4a, "now", 0); + public static final Function AREAS = + new Function(0x4b, "areas", 0xff); + public static final Function ROWS = + new Function(0x4c, "rows", 1); + public static final Function COLUMNS = + new Function(0x4d, "columns", 0xff); + public static final Function OFFSET = + new Function(0x4e, "offset", 0xff); + //public static final Function ABSREF = new Function(0x4f, "ABSREF",); + //public static final Function RELREF = new Function(0x50, "RELREF",); + //public static final Function ARGUMENT = new Function(0x51,"ARGUMENT",); + public static final Function SEARCH = new Function(0x52, "search", 0xff); + public static final Function TRANSPOSE = + new Function(0x53, "transpose", 0xff); + public static final Function ERROR = + new Function(0x54, "error", 1); + //public static final Function STEP = new Function(0x55, "STEP",); + public static final Function TYPE = + new Function(0x56, "type", 1); + //public static final Function ECHO = new Function(0x57, "ECHO",); + //public static final Function SETNAME = new Function(0x58, "SETNAME",); + //public static final Function CALLER = new Function(0x59, "CALLER",); + //public static final Function DEREF = new Function(0x5a, "DEREF",); + //public static final Function WINDOWS = new Function(0x5b, "WINDOWS",); + //public static final Function SERIES = new Function(0x5c, "SERIES",); + //public static final Function DOCUMENTS = new Function(0x5d,"DOCUMENTS",); + //public static final Function ACTIVECELL = new Function(0x5e,"ACTIVECELL",); + //public static final Function SELECTION = new Function(0x5f,"SELECTION",); + //public static final Function RESULT = new Function(0x60, "RESULT",); + public static final Function ATAN2 = + new Function(0x61, "atan2", 1); + public static final Function ASIN = + new Function(0x62, "asin", 1); + public static final Function ACOS = + new Function(0x63, "acos", 1); + public static final Function CHOOSE = + new Function(0x64, "choose", 0xff); + public static final Function HLOOKUP = + new Function(0x65, "hlookup", 0xff); + public static final Function VLOOKUP = + new Function(0x66, "vlookup", 0xff); + //public static final Function LINKS = new Function(0x67, "LINKS",); + //public static final Function INPUT = new Function(0x68, "INPUT",); + public static final Function ISREF = + new Function(0x69, "isref", 1); + //public static final Function GETFORMULA = new Function(0x6a,"GETFORMULA",); + //public static final Function GETNAME = new Function(0x6b, "GETNAME",); + //public static final Function SETVALUE = new Function(0x6c,"SETVALUE",); + public static final Function LOG = + new Function(0x6d, "log", 0xff); + //public static final Function EXEC = new Function(0x6e, "EXEC",); + public static final Function CHAR = + new Function(0x6f, "char", 1); + public static final Function LOWER = + new Function(0x70, "lower", 1); + public static final Function UPPER = + new Function(0x71, "upper", 1); + public static final Function PROPER = + new Function(0x72, "proper", 1); + public static final Function LEFT = + new Function(0x73, "left", 0xff); + public static final Function RIGHT = + new Function(0x74, "right", 0xff); + public static final Function EXACT = + new Function(0x75, "exact", 2); + public static final Function TRIM = + new Function(0x76, "trim", 1); + public static final Function REPLACE = + new Function(0x77, "replace", 4); + public static final Function SUBSTITUTE = + new Function(0x78, "substitute", 0xff); + public static final Function CODE = + new Function(0x79, "code", 1); + //public static final Function NAMES = new Function(0x7a, "NAMES",); + //public static final Function DIRECTORY = new Function(0x7b,"DIRECTORY",); + public static final Function FIND = + new Function(0x7c, "find", 0xff); + public static final Function CELL = + new Function(0x7d, "cell", 2); + public static final Function ISERR = + new Function(0x7e, "iserr", 1); + public static final Function ISTEXT = + new Function(0x7f, "istext", 1); + public static final Function ISNUMBER = + new Function(0x80, "isnumber", 1); + public static final Function ISBLANK = + new Function(0x81, "isblank", 1); + public static final Function T = + new Function(0x82, "t", 1); + public static final Function N = + new Function(0x83, "n", 1); + //public static final Function FOPEN = new Function(0x84, "FOPEN",); + //public static final Function FCLOSE = new Function(0x85, "FCLOSE",); + //public static final Function FSIZE = new Function(0x86, "FSIZE",); + //public static final Function FREADLN = new Function(0x87, "FREADLN",); + //public static final Function FREAD = new Function(0x88, "FREAD",); + //public static final Function FWRITELN = new Function(0x89,"FWRITELN",); + //public static final Function FWRITE = new Function(0x8a, "FWRITE",); + //public static final Function FPOS = new Function(0x8b, "FPOS",); + public static final Function DATEVALUE = + new Function(0x8c, "datevalue", 1); + public static final Function TIMEVALUE = + new Function(0x8d, "timevalue", 1); + public static final Function SLN = + new Function(0x8e, "sln", 3); + public static final Function SYD = + new Function(0x8f, "syd", 3); + public static final Function DDB = + new Function(0x90, "ddb", 0xff); + //public static final Function GETDEF = new Function(0x91, "GETDEF",); + //public static final Function REFTEXT = new Function(0x92, "REFTEXT",); + //public static final Function TEXTREF = new Function(0x93, "TEXTREF",); + public static final Function INDIRECT = + new Function(0x94, "indirect", 0xff); + //public static final Function REGISTER = new Function(0x95,"REGISTER",); + //public static final Function CALL = new Function(0x96, "CALL",); + //public static final Function ADDBAR = new Function(0x97, "ADDBAR",); + //public static final Function ADDMENU = new Function(0x98, "ADDMENU",); + //public static final Function ADDCOMMAND = + // new Function(0x99,"ADDCOMMAND",); + //public static final Function ENABLECOMMAND = + // new Function(0x9a,"ENABLECOMMAND",); + //public static final Function CHECKCOMMAND = + // new Function(0x9b,"CHECKCOMMAND",); + //public static final Function RENAMECOMMAND = + // new Function(0x9c,"RENAMECOMMAND",); + //public static final Function SHOWBAR = new Function(0x9d, "SHOWBAR",); + //public static final Function DELETEMENU = + // new Function(0x9e,"DELETEMENU",); + //public static final Function DELETECOMMAND = + // new Function(0x9f,"DELETECOMMAND",); + //public static final Function GETCHARTITEM = + // new Function(0xa0,"GETCHARTITEM",); + //public static final Function DIALOGBOX = new Function(0xa1,"DIALOGBOX",); + public static final Function CLEAN = + new Function(0xa2, "clean", 1); + public static final Function MDETERM = + new Function(0xa3, "mdeterm", 0xff); + public static final Function MINVERSE = + new Function(0xa4, "minverse", 0xff); + public static final Function MMULT = + new Function(0xa5, "mmult", 0xff); + //public static final Function FILES = new Function(0xa6, "FILES", + + public static final Function IPMT = + new Function(0xa7, "ipmt", 0xff); + public static final Function PPMT = + new Function(0xa8, "ppmt", 0xff); + public static final Function COUNTA = + new Function(0xa9, "counta", 0xff); + public static final Function PRODUCT = + new Function(0xb7, "product", 0xff); + public static final Function FACT = + new Function(0xb8, "fact", 1); + //public static final Function GETCELL = new Function(0xb9, "GETCELL",); + //public static final Function GETWORKSPACE = + // new Function(0xba,"GETWORKSPACE",); + //public static final Function GETWINDOW = new Function(0xbb,"GETWINDOW",); + //public static final Function GETDOCUMENT = + // new Function(0xbc,"GETDOCUMENT",); + public static final Function DPRODUCT = + new Function(0xbd, "dproduct", 3); + public static final Function ISNONTEXT = + new Function(0xbe, "isnontext", 1); + //public static final Function GETNOTE = new Function(0xbf, "GETNOTE",); + //public static final Function NOTE = new Function(0xc0, "NOTE",); + public static final Function STDEVP = + new Function(0xc1, "stdevp", 0xff); + public static final Function VARP = + new Function(0xc2, "varp", 0xff); + public static final Function DSTDEVP = + new Function(0xc3, "dstdevp", 0xff); + public static final Function DVARP = + new Function(0xc4, "dvarp", 0xff); + public static final Function TRUNC = + new Function(0xc5, "trunc", 0xff); + public static final Function ISLOGICAL = + new Function(0xc6, "islogical", 1); + public static final Function DCOUNTA = + new Function(0xc7, "dcounta", 0xff); + public static final Function FINDB = + new Function(0xcd, "findb", 0xff); + public static final Function SEARCHB = + new Function(0xce, "searchb", 3); + public static final Function REPLACEB = + new Function(0xcf, "replaceb", 4); + public static final Function LEFTB = + new Function(0xd0, "leftb", 0xff); + public static final Function RIGHTB = + new Function(0xd1, "rightb", 0xff); + public static final Function MIDB = + new Function(0xd2, "midb", 3); + public static final Function LENB = + new Function(0xd3, "lenb", 1); + public static final Function ROUNDUP = + new Function(0xd4, "roundup", 2); + public static final Function ROUNDDOWN = + new Function(0xd5, "rounddown", 2); + public static final Function RANK = + new Function(0xd8, "rank", 0xff); + public static final Function ADDRESS = + new Function(0xdb, "address", 0xff); + public static final Function AYS360 = + new Function(0xdc, "days360", 0xff); + public static final Function ODAY = + new Function(0xdd, "today", 0); + public static final Function VDB = + new Function(0xde, "vdb", 0xff); + public static final Function MEDIAN = + new Function(0xe3, "median", 0xff); + public static final Function SUMPRODUCT = + new Function(0xe4, "sumproduct", 0xff); + public static final Function SINH = + new Function(0xe5, "sinh", 1); + public static final Function COSH = + new Function(0xe6, "cosh", 1); + public static final Function TANH = + new Function(0xe7, "tanh", 1); + public static final Function ASINH = + new Function(0xe8, "asinh", 1); + public static final Function ACOSH = + new Function(0xe9, "acosh", 1); + public static final Function ATANH = + new Function(0xea, "atanh", 1); + public static final Function INFO = + new Function(0xf4, "info", 1); + public static final Function AVEDEV = + new Function(0x10d, "avedev", 0XFF); + public static final Function BETADIST = + new Function(0x10e, "betadist", 0XFF); + public static final Function GAMMALN = + new Function(0x10f, "gammaln", 1); + public static final Function BETAINV = + new Function(0x110, "betainv", 0XFF); + public static final Function BINOMDIST = + new Function(0x111, "binomdist", 4); + public static final Function CHIDIST = + new Function(0x112, "chidist", 2); + public static final Function CHIINV = + new Function(0x113, "chiinv", 2); + public static final Function COMBIN = + new Function(0x114, "combin", 2); + public static final Function CONFIDENCE = + new Function(0x115, "confidence", 3); + public static final Function CRITBINOM = + new Function(0x116, "critbinom", 3); + public static final Function EVEN = + new Function(0x117, "even", 1); + public static final Function EXPONDIST = + new Function(0x118, "expondist", 3); + public static final Function FDIST = + new Function(0x119, "fdist", 3); + public static final Function FINV = + new Function(0x11a, "finv", 3); + public static final Function FISHER = + new Function(0x11b, "fisher", 1); + public static final Function FISHERINV = + new Function(0x11c, "fisherinv", 1); + public static final Function FLOOR = + new Function(0x11d, "floor", 2); + public static final Function GAMMADIST = + new Function(0x11e, "gammadist", 4); + public static final Function GAMMAINV = + new Function(0x11f, "gammainv", 3); + public static final Function CEILING = + new Function(0x120, "ceiling", 2); + public static final Function HYPGEOMDIST = + new Function(0x121, "hypgeomdist", 4); + public static final Function LOGNORMDIST = + new Function(0x122, "lognormdist", 3); + public static final Function LOGINV = + new Function(0x123, "loginv", 3); + public static final Function NEGBINOMDIST = + new Function(0x124, "negbinomdist", 3); + public static final Function NORMDIST = + new Function(0x125, "normdist", 4); + public static final Function NORMSDIST = + new Function(0x126, "normsdist", 1); + public static final Function NORMINV = + new Function(0x127, "norminv", 3); + public static final Function NORMSINV = + new Function(0x128, "normsinv", 1); + public static final Function STANDARDIZE = + new Function(0x129, "standardize", 3); + public static final Function ODD = + new Function(0x12a, "odd", 1); + public static final Function PERMUT = + new Function(0x12b, "permut", 2); + public static final Function POISSON = + new Function(0x12c, "poisson", 3); + public static final Function TDIST = + new Function(0x12d, "tdist", 3); + public static final Function WEIBULL = + new Function(0x12e, "weibull", 4); + public static final Function SUMXMY2 = + new Function(303, "sumxmy2", 0xff); + public static final Function SUMX2MY2 = + new Function(304, "sumx2my2", 0xff); + public static final Function SUMX2PY2 = + new Function(305, "sumx2py2", 0xff); + public static final Function CHITEST = + new Function(0x132, "chitest", 0xff); + public static final Function CORREL = + new Function(0x133, "correl", 0xff); + public static final Function COVAR = + new Function(0x134, "covar", 0xff); + public static final Function FORECAST = + new Function(0x135, "forecast", 0xff); + public static final Function FTEST = + new Function(0x136, "ftest", 0xff); + public static final Function INTERCEPT = + new Function(0x137, "intercept", 0xff); + public static final Function PEARSON = + new Function(0x138, "pearson", 0xff); + public static final Function RSQ = + new Function(0x139, "rsq", 0xff); + public static final Function STEYX = + new Function(0x13a, "steyx", 0xff); + public static final Function SLOPE = + new Function(0x13b, "slope", 2); + public static final Function TTEST = + new Function(0x13c, "ttest", 0xff); + public static final Function PROB = + new Function(0x13d, "prob", 0xff); + public static final Function DEVSQ = + new Function(0x13e, "devsq", 0xff); + public static final Function GEOMEAN = + new Function(0x13f, "geomean", 0xff); + public static final Function HARMEAN = + new Function(0x140, "harmean", 0xff); + public static final Function SUMSQ = + new Function(0x141, "sumsq", 0xff); + public static final Function KURT = + new Function(0x142, "kurt", 0xff); + public static final Function SKEW = + new Function(0x143, "skew", 0xff); + public static final Function ZTEST = + new Function(0x144, "ztest", 0xff); + public static final Function LARGE = + new Function(0x145, "large", 0xff); + public static final Function SMALL = + new Function(0x146, "small", 0xff); + public static final Function QUARTILE = + new Function(0x147, "quartile", 0xff); + public static final Function PERCENTILE = + new Function(0x148, "percentile", 0xff); + public static final Function PERCENTRANK = + new Function(0x149, "percentrank", 0xff); + public static final Function MODE = + new Function(0x14a, "mode", 0xff); + public static final Function TRIMMEAN = + new Function(0x14b, "trimmean", 0xff); + public static final Function TINV = + new Function(0x14c, "tinv", 2); + public static final Function CONCATENATE = + new Function(0x150, "concatenate", 0xff); + public static final Function POWER = + new Function(0x151, "power", 2); + public static final Function RADIANS = + new Function(0x156, "radians", 1); + public static final Function DEGREES = + new Function(0x157, "degrees", 1); + public static final Function SUBTOTAL = + new Function(0x158, "subtotal", 0xff); + public static final Function SUMIF = + new Function(0x159, "sumif", 0xff); + public static final Function COUNTIF = + new Function(0x15a, "countif", 2); + public static final Function COUNTBLANK = + new Function(0x15b, "countblank", 1); + public static final Function HYPERLINK = + new Function(0x167, "hyperlink", 2); + public static final Function AVERAGEA = + new Function(0x169, "averagea", 0xff); + public static final Function MAXA = + new Function(0x16a, "maxa", 0xff); + public static final Function MINA = + new Function(0x16b, "mina", 0xff); + public static final Function STDEVPA = + new Function(0x16c, "stdevpa", 0xff); + public static final Function VARPA = + new Function(0x16d, "varpa", 0xff); + public static final Function STDEVA = + new Function(0x16e, "stdeva", 0xff); + public static final Function VARA = + new Function(0x16f, "vara", 0xff); + + // If token. This is not an excel assigned number, but one made up + // in order that the if command may be recognized + public static final Function IF = + new Function(0xfffe, "if", 0xff); + + // Unknown token + public static final Function UNKNOWN = new Function(0xffff, "", 0); +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/FunctionNames.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/FunctionNames.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/FunctionNames.java 17 Aug 2012 14:51:15 -0000 1.1 @@ -0,0 +1,104 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +import java.util.HashMap; +import java.util.Locale; +import java.util.ResourceBundle; + +import common.Logger; + +/** + * A class which contains the function names for the current workbook. The + * function names can potentially vary from workbook to workbook depending + * on the locale + */ +public class FunctionNames +{ + /** + * The logger class + */ + private static Logger logger = Logger.getLogger(FunctionNames.class); + + /** + * A hash mapping keyed on the function and returning its locale specific + * name + */ + private HashMap names; + + /** + * A hash mapping keyed on the locale specific name and returning the + * function + */ + private HashMap functions; + + /** + * Constructor + * + * @param l the locale + */ + public FunctionNames(Locale l) + { + ResourceBundle rb = ResourceBundle.getBundle("functions", l); + Function[] allfunctions = Function.getFunctions(); + names = new HashMap(allfunctions.length); + functions = new HashMap(allfunctions.length); + + // Iterate through all the functions, adding them to the hash maps + Function f = null; + String n = null; + String propname = null; + for (int i = 0; i < allfunctions.length; i++) + { + f = allfunctions[i]; + propname = f.getPropertyName(); + + n = propname.length() != 0 ? rb.getString(propname) : null; + + if (n != null) + { + names.put(f, n); + functions.put(n, f); + } + } + } + + /** + * Gets the function for the specified name + * + * @param s the string + * @return the function + */ + Function getFunction(String s) + { + return (Function) functions.get(s); + } + + /** + * Gets the name for the function + * + * @param f the function + * @return the string + */ + String getName(Function f) + { + return (String) names.get(f); + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/GreaterEqual.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/GreaterEqual.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/GreaterEqual.java 17 Aug 2012 14:51:15 -0000 1.1 @@ -0,0 +1,59 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +/** + * A cell reference in a formula + */ +class GreaterEqual extends BinaryOperator implements ParsedThing +{ + /** + * Constructor + */ + public GreaterEqual() + { + } + + public String getSymbol() + { + return ">="; + } + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + Token getToken() + { + return Token.GREATER_EQUAL; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() + { + return 5; + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/GreaterThan.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/GreaterThan.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/GreaterThan.java 17 Aug 2012 14:51:15 -0000 1.1 @@ -0,0 +1,59 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +/** + * A cell reference in a formula + */ +class GreaterThan extends BinaryOperator implements ParsedThing +{ + /** + * Constructor + */ + public GreaterThan() + { + } + + public String getSymbol() + { + return ">"; + } + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + Token getToken() + { + return Token.GREATER_THAN; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() + { + return 5; + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/IntegerValue.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/IntegerValue.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/IntegerValue.java 17 Aug 2012 14:51:16 -0000 1.1 @@ -0,0 +1,130 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +import common.Logger; + +import jxl.biff.IntegerHelper; + +/** + * A cell reference in a formula + */ +class IntegerValue extends NumberValue implements ParsedThing +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(IntegerValue.class); + + /** + * The value of this integer + */ + private double value; + + /** + * Flag which indicates whether or not this integer is out range + */ + private boolean outOfRange; + + /** + * Constructor + */ + public IntegerValue() + { + outOfRange = false; + } + + /** + * Constructor for an integer value being read from a string + */ + public IntegerValue(String s) + { + try + { + value = Integer.parseInt(s); + } + catch (NumberFormatException e) + { + logger.warn(e, e); + value = 0; + } + + short v = (short) value; + outOfRange = (value != v); + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) + { + value = IntegerHelper.getInt(data[pos], data[pos+1]); + + return 2; + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() + { + byte[] data = new byte[3]; + data[0] = Token.INTEGER.getCode(); + + IntegerHelper.getTwoBytes((int) value, data, 1); + + return data; + } + + /** + * Accessor for the value + * + * @return the value + */ + public double getValue() + { + return value; + } + + /** + * Accessor for the out of range flag + * + * @return TRUE if the value is out of range for an excel integer + */ + boolean isOutOfRange() + { + return outOfRange; + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing + */ + void handleImportedCellReferences() + { + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/LessEqual.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/LessEqual.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/LessEqual.java 17 Aug 2012 14:51:17 -0000 1.1 @@ -0,0 +1,71 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +/** + * A cell reference in a formula + */ +class LessEqual extends BinaryOperator implements ParsedThing +{ + /** + * Constructor + */ + public LessEqual() + { + } + + public String getSymbol() + { + return "<="; + } + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + Token getToken() + { + return Token.LESS_EQUAL; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() + { + return 5; + } +} + + + + + + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/LessThan.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/LessThan.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/LessThan.java 17 Aug 2012 14:51:16 -0000 1.1 @@ -0,0 +1,59 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +/** + * A cell reference in a formula + */ +class LessThan extends BinaryOperator implements ParsedThing +{ + /** + * Constructor + */ + public LessThan() + { + } + + public String getSymbol() + { + return "<"; + } + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + Token getToken() + { + return Token.LESS_THAN; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() + { + return 5; + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/MemArea.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/MemArea.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/MemArea.java 17 Aug 2012 14:51:16 -0000 1.1 @@ -0,0 +1,75 @@ +/********************************************************************* +* +* Copyright (C) 2007 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +import jxl.biff.IntegerHelper; + +/** + * Indicates that the function doesn't evaluate to a constant reference + */ +class MemArea extends SubExpression +{ + /** + * Constructor + */ + public MemArea() + { + } + + public void getString(StringBuffer buf) + { + ParseItem[] subExpression = getSubExpression(); + + if (subExpression.length == 1) + { + subExpression[0].getString(buf); + } + else if (subExpression.length == 2) + { + subExpression[1].getString(buf); + buf.append(':'); + subExpression[0].getString(buf); + } + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) + { + // For mem areas, the first four bytes are not used + setLength(IntegerHelper.getInt(data[pos+4], data[pos+5])); + return 6; + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing + */ + void handleImportedCellReferences() + { + } +} + Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/MemFunc.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/MemFunc.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/MemFunc.java 17 Aug 2012 14:51:15 -0000 1.1 @@ -0,0 +1,54 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff.formula; + +import java.util.Stack; + +import jxl.biff.IntegerHelper; + +/** + * Indicates that the function doesn't evaluate to a constant reference + */ +class MemFunc extends SubExpression +{ + /** + * Constructor + */ + public MemFunc() + { + } + + public void getString(StringBuffer sb) + { + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing + */ + void handleImportedCellReferences() + { + } +} + + + + Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/Minus.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/Minus.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/Minus.java 17 Aug 2012 14:51:15 -0000 1.1 @@ -0,0 +1,67 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +import common.Logger; +/** + * Ambiguously defined minus operator, used as a place holder when parsing + * string formulas. At this stage it could be either + * a unary or binary operator - the string parser will deduce which and + * create the appropriate type + */ +class Minus extends StringOperator +{ + // The logger + private static Logger logger = Logger.getLogger(StringOperator.class); + + /** + * Constructor + */ + public Minus() + { + super(); + } + + /** + * Abstract method which gets the binary version of this operator + */ + Operator getBinaryOperator() + { + return new Subtract(); + } + + /** + * Abstract method which gets the unary version of this operator + */ + Operator getUnaryOperator() + { + return new UnaryMinus(); + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing, as operators don't have cell references + */ + void handleImportedCellReferences() + { + } + +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/MissingArg.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/MissingArg.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/MissingArg.java 17 Aug 2012 14:51:15 -0000 1.1 @@ -0,0 +1,79 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +/** + * Represents a missing argument in an argument list + */ +class MissingArg extends Operand implements ParsedThing +{ + /** + * Constructor + */ + public MissingArg() + { + } + + /** + * Reads the ptg data from the array starting at the specified position. + * A missing argument contains no associated data + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) + { + return 0; + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() + { + byte[] data = new byte[1]; + data[0] = Token.MISSING_ARG.getCode(); + + return data; + } + + /** + * Abstract method implementation to get the string equivalent of this + * token + * + * @param buf the string to append to + */ + public void getString(StringBuffer buf) + { + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing + */ + void handleImportedCellReferences() + { + } +} + Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/Multiply.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/Multiply.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/Multiply.java 17 Aug 2012 14:51:16 -0000 1.1 @@ -0,0 +1,59 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +/** + * A cell reference in a formula + */ +class Multiply extends BinaryOperator implements ParsedThing +{ + /** + * Constructor + */ + public Multiply() + { + } + + public String getSymbol() + { + return "*"; + } + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + Token getToken() + { + return Token.MULTIPLY; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() + { + return 3; + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/Name.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/Name.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/Name.java 17 Aug 2012 14:51:15 -0000 1.1 @@ -0,0 +1,79 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +import jxl.biff.IntegerHelper; +/** + * A name operand + */ +class Name extends Operand implements ParsedThing +{ + /** + * Constructor + */ + public Name() + { + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) + { + return 6; + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() + { + byte[] data = new byte[6]; + + return data; + } + + /** + * Abstract method implementation to get the string equivalent of this + * token + * + * @param buf the string to append to + */ + public void getString(StringBuffer buf) + { + buf.append("[Name record not implemented]"); + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Sets this formula to invalid + */ + void handleImportedCellReferences() + { + setInvalid(); + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/NameRange.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/NameRange.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/NameRange.java 17 Aug 2012 14:51:16 -0000 1.1 @@ -0,0 +1,130 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +import common.Assert; + +import jxl.biff.IntegerHelper; +import jxl.biff.WorkbookMethods; + +/** + * A name operand + */ +class NameRange extends Operand implements ParsedThing +{ + /** + * A handle to the name table + */ + private WorkbookMethods nameTable; + + /** + * The string name + */ + private String name; + + /** + * The index into the name table + */ + private int index; + + /** + * Constructor + */ + public NameRange(WorkbookMethods nt) + { + nameTable = nt; + Assert.verify(nameTable != null); + } + + /** + * Constructor when parsing a string via the api + * + * @param nm the name string + * @param nt the name table + */ + public NameRange(String nm, WorkbookMethods nt) throws FormulaException + { + name = nm; + nameTable = nt; + + index = nameTable.getNameIndex(name); + + if (index < 0 ) + { + throw new FormulaException(FormulaException.CELL_NAME_NOT_FOUND, name); + } + + index += 1; // indexes are 1-based + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) + { + index = IntegerHelper.getInt(data[pos], data[pos+1]); + + name = nameTable.getName(index - 1); // ilbl is 1-based + + return 4; + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() + { + byte[] data = new byte[5]; + data[0] = Token.NAMED_RANGE.getCode(); + + IntegerHelper.getTwoBytes(index, data, 1); + + return data; + } + + /** + * Abstract method implementation to get the string equivalent of this + * token + * + * @param buf the string to append to + */ + public void getString(StringBuffer buf) + { + buf.append(name); + } + + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Flags the formula as invalid + */ + void handleImportedCellReferences() + { + setInvalid(); + } + +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/NotEqual.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/NotEqual.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/NotEqual.java 17 Aug 2012 14:51:16 -0000 1.1 @@ -0,0 +1,60 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +/** + * A cell reference in a formula + */ +class NotEqual extends BinaryOperator implements ParsedThing +{ + /** + * Constructor + */ + public NotEqual() + { + } + + public String getSymbol() + { + return "<>"; + } + + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + Token getToken() + { + return Token.NOT_EQUAL; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() + { + return 5; + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/NumberValue.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/NumberValue.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/NumberValue.java 17 Aug 2012 14:51:16 -0000 1.1 @@ -0,0 +1,37 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +/** + * A constant numerical value in a formula + */ +abstract class NumberValue extends Operand implements ParsedThing +{ + protected NumberValue() + { + } + + public abstract double getValue(); + + public void getString(StringBuffer buf) + { + buf.append(Double.toString(getValue())); + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/OpenParentheses.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/OpenParentheses.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/OpenParentheses.java 17 Aug 2012 14:51:16 -0000 1.1 @@ -0,0 +1,34 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +/** + * A dummy token used when parsing strings in order to indicate the + * opening of some parenthesees + */ +class OpenParentheses extends StringParseItem +{ + /** + * Constructor + */ + public OpenParentheses() + { + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/Operand.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/Operand.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/Operand.java 17 Aug 2012 14:51:16 -0000 1.1 @@ -0,0 +1,109 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +/** + * An operand in the parse tree. Operands represent leaf nodes in the + * tree, and may not have children + * Operands include numerical values, cell references and ranges + */ +abstract class Operand extends ParseItem +{ + /** + * Constructor + */ + public Operand() + { + } + + /** + * Default behaviour is to do nothing + * + * @param colAdjust the amount to add on to each relative cell reference + * @param rowAdjust the amount to add on to each relative row reference + */ + public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) + { + } + + /** + * Default behaviour is to do nothing + * + * @param sheetIndex the sheet on which the column was inserted + * @param col the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnInserted(int sheetIndex, int col, boolean currentSheet) + { + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param col the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnRemoved(int sheetIndex, int col, boolean currentSheet) + { + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was inserted + * @param row the row number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowInserted(int sheetIndex, int row, boolean currentSheet) + { + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was removed + * @param row the row number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowRemoved(int sheetIndex, int row, boolean currentSheet) + { + } +} + + + + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/Operator.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/Operator.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/Operator.java 17 Aug 2012 14:51:16 -0000 1.1 @@ -0,0 +1,90 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +import java.util.Stack; + +/** + * An operator is a node in a parse tree. Its children can be other + * operators or operands + * Arithmetic operators and functions are all considered operators + */ +abstract class Operator extends ParseItem +{ + /** + * The items which this operator manipulates. There will be at most two + */ + private ParseItem[] operands; + + /** + * Constructor + */ + public Operator() + { + operands = new ParseItem[0]; + } + + /** + * Tells the operands to use the alternate code + */ + protected void setOperandAlternateCode() + { + for (int i = 0 ; i < operands.length ; i++) + { + operands[i].setAlternateCode(); + } + } + + /** + * Adds operands to this item + */ + protected void add(ParseItem n) + { + n.setParent(this); + + // Grow the array + ParseItem[] newOperands = new ParseItem[operands.length + 1]; + System.arraycopy(operands, 0, newOperands, 0, operands.length); + newOperands[operands.length] = n; + operands = newOperands; + } + + /** + * Gets the operands for this operator from the stack + */ + public abstract void getOperands(Stack s); + + /** + * Gets the operands ie. the children of the node + */ + protected ParseItem[] getOperands() + { + return operands; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + abstract int getPrecedence(); + +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/Parenthesis.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/Parenthesis.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/Parenthesis.java 17 Aug 2012 14:51:16 -0000 1.1 @@ -0,0 +1,193 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +import java.util.Stack; + +/** + * A cell reference in a formula + */ +class Parenthesis extends Operator implements ParsedThing +{ + /** + * Constructor + */ + public Parenthesis() + { + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) + { + return 0; + } + + /** + * Gets the operands for this operator from the stack + */ + public void getOperands(Stack s) + { + ParseItem pi = (ParseItem) s.pop(); + + add(pi); + } + + public void getString(StringBuffer buf) + { + ParseItem[] operands = getOperands(); + buf.append('('); + operands[0].getString(buf); + buf.append(')'); + } + + /** + * Adjusts all the relative cell references in this formula by the + * amount specified. Used when copying formulas + * + * @param colAdjust the amount to add on to each relative cell reference + * @param rowAdjust the amount to add on to each relative row reference + */ + public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) + { + ParseItem[] operands = getOperands(); + operands[0].adjustRelativeCellReferences(colAdjust, rowAdjust); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param col the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnInserted(int sheetIndex, int col, boolean currentSheet) + { + ParseItem[] operands = getOperands(); + operands[0].columnInserted(sheetIndex, col, currentSheet); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param col the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnRemoved(int sheetIndex, int col, boolean currentSheet) + { + ParseItem[] operands = getOperands(); + operands[0].columnRemoved(sheetIndex, col, currentSheet); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was inserted + * @param row the row number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowInserted(int sheetIndex, int row, boolean currentSheet) + { + ParseItem[] operands = getOperands(); + operands[0].rowInserted(sheetIndex, row, currentSheet); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was removed + * @param row the row number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowRemoved(int sheetIndex, int row, boolean currentSheet) + { + ParseItem[] operands = getOperands(); + operands[0].rowRemoved(sheetIndex, row, currentSheet); + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing, as operators don't have cell references + */ + void handleImportedCellReferences() + { + ParseItem[] operands = getOperands(); + operands[0].handleImportedCellReferences(); + } + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + Token getToken() + { + return Token.PARENTHESIS; + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() + { + // Get the data for the operands + ParseItem[] operands = getOperands(); + byte[] data = operands[0].getBytes(); + + // Add on the operator byte + byte[] newdata = new byte[data.length + 1]; + System.arraycopy(data, 0, newdata, 0, data.length); + newdata[data.length] = getToken().getCode(); + + return newdata; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() + { + return 4; + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/ParseItem.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/ParseItem.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/ParseItem.java 17 Aug 2012 14:51:14 -0000 1.1 @@ -0,0 +1,211 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +import common.Logger; + +/** + * Abstract base class for an item in a formula parse tree + */ +abstract class ParseItem +{ + // The logger + private static Logger logger = Logger.getLogger(ParseItem.class); + + /** + * The parent of this parse item + */ + private ParseItem parent; + + /** + * Volatile flag + */ + private boolean volatileFunction; + + /** + * Indicates that the alternative token code should be used + */ + private boolean alternateCode; + + /** + * Indicates whether this tree represents a valid formula or not. If not + * the parser replaces it with a valid one + */ + private boolean valid; + + /** + * Constructor + */ + public ParseItem() + { + volatileFunction = false; + alternateCode = false; + valid = true; + } + + /** + * Called by this class to initialize the parent + */ + protected void setParent(ParseItem p) + { + parent = p; + } + + /** + * Sets the volatile flag and ripples all the way up the parse tree + */ + protected void setVolatile() + { + volatileFunction = true; + if (parent != null && !parent.isVolatile()) + { + parent.setVolatile(); + } + } + + /** + * Sets the invalid flag and ripples all the way up the parse tree + */ + protected final void setInvalid() + { + valid = false; + if (parent != null) + { + parent.setInvalid(); + } + } + + /** + * Accessor for the volatile function + * + * @return TRUE if the formula is volatile, FALSE otherwise + */ + final boolean isVolatile() + { + return volatileFunction; + } + + /** + * Accessor for the volatile function + * + * @return TRUE if the formula is volatile, FALSE otherwise + */ + final boolean isValid() + { + return valid; + } + + /** + * Gets the string representation of this item + * @param ws the workbook settings + */ + abstract void getString(StringBuffer buf); + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + abstract byte[] getBytes(); + + /** + * Adjusts all the relative cell references in this formula by the + * amount specified. Used when copying formulas + * + * @param colAdjust the amount to add on to each relative cell reference + * @param rowAdjust the amount to add on to each relative row reference + */ + abstract void adjustRelativeCellReferences(int colAdjust, int rowAdjust); + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param col the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + abstract void columnInserted(int sheetIndex, int col, boolean currentSheet); + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param col the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + abstract void columnRemoved(int sheetIndex, int col, boolean currentSheet); + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was inserted + * @param row the row number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + abstract void rowInserted(int sheetIndex, int row, boolean currentSheet); + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was removed + * @param row the row number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + abstract void rowRemoved(int sheetIndex, int row, boolean currentSheet); + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + */ + abstract void handleImportedCellReferences(); + + /** + * Tells the operands to use the alternate code + */ + protected void setAlternateCode() + { + alternateCode = true; + } + + /** + * Accessor for the alternate code flag + * + * @return TRUE to use the alternate code, FALSE otherwise + */ + protected final boolean useAlternateCode() + { + return alternateCode; + } +} + + + Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/ParsedThing.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/ParsedThing.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/ParsedThing.java 17 Aug 2012 14:51:16 -0000 1.1 @@ -0,0 +1,35 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +/** + * An interface for an excel ptg + */ +interface ParsedThing +{ + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) throws FormulaException; +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/Parser.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/Parser.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/Parser.java 17 Aug 2012 14:51:15 -0000 1.1 @@ -0,0 +1,114 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +/** + * Interface used by the two different types of formula parser + */ +interface Parser +{ + /** + * Parses the formula + * + * @exception FormulaException if an error occurs + */ + public void parse() throws FormulaException; + + /** + * Gets the string version of the formula + * + * @return the formula as a string + */ + public String getFormula(); + + /** + * Gets the bytes for the formula. This takes into account any + * token mapping necessary because of shared formulas + * + * @return the bytes in RPN + */ + public byte[] getBytes(); + + /** + * Adjusts all the relative cell references in this formula by the + * amount specified. + * + * @param colAdjust + * @param rowAdjust + */ + public void adjustRelativeCellReferences(int colAdjust, int rowAdjust); + + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param col the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + public void columnInserted(int sheetIndex, int col, boolean currentSheet); + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param col the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + public void columnRemoved(int sheetIndex, int col, boolean currentSheet); + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param row the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + public void rowInserted(int sheetIndex, int row, boolean currentSheet); + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param row the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + public void rowRemoved(int sheetIndex, int row, boolean currentSheet); + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * + * @return TRUE if the formula is valid import, FALSE otherwise + */ + public boolean handleImportedCellReferences(); +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/Percent.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/Percent.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/Percent.java 17 Aug 2012 14:51:15 -0000 1.1 @@ -0,0 +1,77 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +/** + * A cell reference in a formula + */ +class Percent extends UnaryOperator implements ParsedThing +{ + /** + * Constructor + */ + public Percent() + { + } + + public String getSymbol() + { + return "%"; + } + + public void getString(StringBuffer buf) + { + ParseItem[] operands = getOperands(); + operands[0].getString(buf); + buf.append(getSymbol()); + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing, as operators don't have cell references + */ + void handleImportedCellReferences() + { + ParseItem[] operands = getOperands(); + operands[0].handleImportedCellReferences(); + } + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + Token getToken() + { + return Token.PERCENT; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() + { + return 5; + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/Plus.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/Plus.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/Plus.java 17 Aug 2012 14:51:16 -0000 1.1 @@ -0,0 +1,63 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +/** + * Ambiguously defined plus operator, used as a place holder when parsing + * string formulas. At this stage it could be either + * a unary or binary operator - the string parser will deduce which and + * create the appropriate type + */ +class Plus extends StringOperator +{ + /** + * Constructor + */ + public Plus() + { + super(); + } + + /** + * Abstract method which gets the binary version of this operator + */ + Operator getBinaryOperator() + { + return new Add(); + } + + /** + * Abstract method which gets the unary version of this operator + */ + Operator getUnaryOperator() + { + return new UnaryPlus(); + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing, as operators don't have cell references + */ + void handleImportedCellReferences() + { + } + +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/Power.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/Power.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/Power.java 17 Aug 2012 14:51:16 -0000 1.1 @@ -0,0 +1,59 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +/** + * A cell reference in a formula + */ +class Power extends BinaryOperator implements ParsedThing +{ + /** + * Constructor + */ + public Power() + { + } + + public String getSymbol() + { + return "^"; + } + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + Token getToken() + { + return Token.POWER; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() + { + return 1; + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/RangeSeparator.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/RangeSeparator.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/RangeSeparator.java 17 Aug 2012 14:51:14 -0000 1.1 @@ -0,0 +1,87 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +import jxl.biff.IntegerHelper; + +/** + * A "holding" token for a range separator. This token gets instantiated + * when the lexical analyzer can't distinguish a range cleanly, eg in the + * case where where one of the identifiers of the range is a formula + */ +class RangeSeparator extends BinaryOperator implements ParsedThing +{ + /** + * Constructor + */ + public RangeSeparator() + { + } + + public String getSymbol() + { + return ":"; + } + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + Token getToken() + { + return Token.RANGE; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() + { + return 1; + } + + /** + * Overrides the getBytes() method in the base class and prepends the + * memFunc token + * + * @return the bytes + */ + byte[] getBytes() + { + setVolatile(); + setOperandAlternateCode(); + + byte[] funcBytes = super.getBytes(); + + byte[] bytes = new byte[funcBytes.length+3]; + System.arraycopy(funcBytes, 0, bytes, 3, funcBytes.length); + + // Indicate the mem func + bytes[0] = Token.MEM_FUNC.getCode(); + IntegerHelper.getTwoBytes(funcBytes.length, bytes, 1); + + return bytes; + } + +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/SharedFormulaArea.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/SharedFormulaArea.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/SharedFormulaArea.java 17 Aug 2012 14:51:14 -0000 1.1 @@ -0,0 +1,166 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +import jxl.Cell; +import jxl.biff.CellReferenceHelper; +import jxl.biff.IntegerHelper; + +/** + * A cell reference in a formula + */ +class SharedFormulaArea extends Operand implements ParsedThing +{ + private int columnFirst; + private int rowFirst; + private int columnLast; + private int rowLast; + + private boolean columnFirstRelative; + private boolean rowFirstRelative; + private boolean columnLastRelative; + private boolean rowLastRelative; + + /** + * The cell containing the formula. Stored in order to determine + * relative cell values + */ + private Cell relativeTo; + + /** + * Constructor + * + * @param the cell the formula is relative to + */ + public SharedFormulaArea(Cell rt) + { + relativeTo = rt; + } + + int getFirstColumn() + { + return columnFirst; + } + + int getFirstRow() + { + return rowFirst; + } + + int getLastColumn() + { + return columnLast; + } + + int getLastRow() + { + return rowLast; + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) + { + // Preserve signage on column and row values, because they will + // probably be relative + + rowFirst = IntegerHelper.getShort(data[pos], data[pos+1]); + rowLast = IntegerHelper.getShort(data[pos+2], data[pos+3]); + + int columnMask = IntegerHelper.getInt(data[pos+4], data[pos+5]); + columnFirst = columnMask & 0x00ff; + columnFirstRelative = ((columnMask & 0x4000) != 0); + rowFirstRelative = ((columnMask & 0x8000) != 0); + + if (columnFirstRelative) + { + columnFirst = relativeTo.getColumn() + columnFirst; + } + + if (rowFirstRelative) + { + rowFirst = relativeTo.getRow() + rowFirst; + } + + columnMask = IntegerHelper.getInt(data[pos+6], data[pos+7]); + columnLast = columnMask & 0x00ff; + + columnLastRelative = ((columnMask & 0x4000) != 0); + rowLastRelative = ((columnMask & 0x8000) != 0); + + if (columnLastRelative) + { + columnLast = relativeTo.getColumn() + columnLast; + } + + if (rowLastRelative) + { + rowLast = relativeTo.getRow() + rowLast; + } + + + return 8; + } + + public void getString(StringBuffer buf) + { + CellReferenceHelper.getCellReference(columnFirst, rowFirst, buf); + buf.append(':'); + CellReferenceHelper.getCellReference(columnLast, rowLast, buf); + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() + { + byte[] data = new byte[9]; + data[0] = Token.AREA.getCode(); + + // Use absolute references for columns, so don't bother about + // the col relative/row relative bits + IntegerHelper.getTwoBytes(rowFirst, data, 1); + IntegerHelper.getTwoBytes(rowLast, data, 3); + IntegerHelper.getTwoBytes(columnFirst, data, 5); + IntegerHelper.getTwoBytes(columnLast, data, 7); + + return data; + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing + */ + void handleImportedCellReferences() + { + } + +} + + + Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/SharedFormulaCellReference.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/SharedFormulaCellReference.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/SharedFormulaCellReference.java 17 Aug 2012 14:51:16 -0000 1.1 @@ -0,0 +1,168 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +import common.Logger; + +import jxl.Cell; +import jxl.biff.CellReferenceHelper; +import jxl.biff.IntegerHelper; + + +/** + * A cell reference in a formula + */ +class SharedFormulaCellReference extends Operand implements ParsedThing +{ + // The logger + private static Logger logger = + Logger.getLogger(SharedFormulaCellReference.class); + + /** + * Indicates whether the column reference is relative or absolute + */ + private boolean columnRelative; + + /** + * Indicates whether the row reference is relative or absolute + */ + private boolean rowRelative; + + /** + * The column reference + */ + private int column; + + /** + * The row reference + */ + private int row; + + /** + * The cell containing the formula. Stored in order to determine + * relative cell values + */ + private Cell relativeTo; + + /** + * Constructor + * + * @param the cell the formula is relative to + */ + public SharedFormulaCellReference(Cell rt) + { + relativeTo = rt; + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) + { + // Preserve signage on column and row values, because they will + // probably be relative + row = IntegerHelper.getShort(data[pos], data[pos+1]); + + int columnMask = IntegerHelper.getInt(data[pos+2], data[pos+3]); + + column = (byte) (columnMask & 0xff); + columnRelative = ((columnMask & 0x4000) != 0); + rowRelative = ((columnMask & 0x8000) != 0); + + if (columnRelative && relativeTo != null) + { + column = relativeTo.getColumn() + column; + } + + if (rowRelative && relativeTo != null) + { + row = relativeTo.getRow() + row; + } + + return 4; + } + + public int getColumn() + { + return column; + } + + public int getRow() + { + return row; + } + + public void getString(StringBuffer buf) + { + CellReferenceHelper.getCellReference(column, row, buf); + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() + { + byte[] data = new byte[5]; + data[0] = Token.REF.getCode(); + + IntegerHelper.getTwoBytes(row, data, 1); + + int columnMask = column; + + if (columnRelative) + { + columnMask |= 0x4000; + } + + if (rowRelative) + { + columnMask |= 0x8000; + } + + IntegerHelper.getTwoBytes(columnMask, data, 3); + + return data; + } + + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing + */ + void handleImportedCellReferences() + { + } +} + + + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/StringFormulaParser.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/StringFormulaParser.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/StringFormulaParser.java 17 Aug 2012 14:51:15 -0000 1.1 @@ -0,0 +1,567 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Stack; + +import common.Logger; + +import jxl.WorkbookSettings; +import jxl.biff.WorkbookMethods; + +/** + * Parses a string formula into a parse tree + */ +class StringFormulaParser implements Parser +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(StringFormulaParser.class); + + /** + * The formula string passed to this object + */ + private String formula; + + /** + * The parsed formula string, as retrieved from the parse tree + */ + private String parsedFormula; + + /** + * The parse tree + */ + private ParseItem root; + + /** + * The stack argument used when parsing a function in order to + * pass multiple arguments back to the calling method + */ + private Stack arguments; + + /** + * The workbook settings + */ + private WorkbookSettings settings; + + /** + * A handle to the external sheet + */ + private ExternalSheet externalSheet; + + /** + * A handle to the name table + */ + private WorkbookMethods nameTable; + + /** + * Constructor + * @param f + * @param ws + */ + public StringFormulaParser(String f, + ExternalSheet es, + WorkbookMethods nt, + WorkbookSettings ws) + { + formula = f; + settings = ws; + externalSheet = es; + nameTable = nt; + } + + /** + * Parses the list of tokens + * + * @exception FormulaException + */ + public void parse() throws FormulaException + { + ArrayList tokens = getTokens(); + + Iterator i = tokens.iterator(); + + root = parseCurrent(i); + } + + /** + * Recursively parses the token array. Recursion is used in order + * to evaluate parentheses and function arguments + * + * @param i an iterator of tokens + * @return the root node of the current parse stack + * @exception FormulaException if an error occurs + */ + private ParseItem parseCurrent(Iterator i) throws FormulaException + { + Stack stack = new Stack(); + Stack operators = new Stack(); + Stack args = null; // we usually don't need this + + boolean parenthesesClosed = false; + ParseItem lastParseItem = null; + + while (i.hasNext() && !parenthesesClosed) + { + ParseItem pi = (ParseItem) i.next(); + + if (pi instanceof Operand) + { + handleOperand((Operand) pi, stack); + } + else if (pi instanceof StringFunction) + { + handleFunction((StringFunction) pi, i, stack); + } + else if (pi instanceof Operator) + { + Operator op = (Operator) pi; + + // See if the operator is a binary or unary operator + // It is a unary operator either if the stack is empty, or if + // the last thing off the stack was another operator + if (op instanceof StringOperator) + { + StringOperator sop = (StringOperator) op; + if (stack.isEmpty() || lastParseItem instanceof Operator) + { + op = sop.getUnaryOperator(); + } + else + { + op = sop.getBinaryOperator(); + } + } + + if (operators.empty()) + { + // nothing much going on, so do nothing for the time being + operators.push(op); + } + else + { + Operator operator = (Operator) operators.peek(); + + // If the last operator has a higher precedence then add this to + // the operator stack and wait + if (op.getPrecedence() < operator.getPrecedence()) + { + operators.push(op); + } + else if (op.getPrecedence() == operator.getPrecedence() && + op instanceof UnaryOperator) + { + // The operators are of equal precedence, but because it is a + // unary operator the operand isn't available yet, so put it on + // the stack + operators.push(op); + } + else + { + // The operator is of a lower precedence so we can sort out + // some of the items on the stack + operators.pop(); // remove the operator from the stack + operator.getOperands(stack); + stack.push(operator); + operators.push(op); + } + } + } + else if (pi instanceof ArgumentSeparator) + { + // Clean up any remaining items on this stack + while (!operators.isEmpty()) + { + Operator o = (Operator) operators.pop(); + o.getOperands(stack); + stack.push(o); + } + + // Add it to the argument stack. Create the argument stack + // if necessary. Items will be stored on the argument stack in + // reverse order + if (args == null) + { + args = new Stack(); + } + + args.push(stack.pop()); + stack.clear(); + } + else if (pi instanceof OpenParentheses) + { + ParseItem pi2 = parseCurrent(i); + Parenthesis p = new Parenthesis(); + pi2.setParent(p); + p.add(pi2); + stack.push(p); + } + else if (pi instanceof CloseParentheses) + { + parenthesesClosed = true; + } + + lastParseItem = pi; + } + + while (!operators.isEmpty()) + { + Operator o = (Operator) operators.pop(); + o.getOperands(stack); + stack.push(o); + } + + ParseItem rt = !stack.empty()? (ParseItem) stack.pop():null; + + // if the argument stack is not null, then add it to that stack + // as well for good measure + if (args != null && rt != null) + { + args.push(rt); + } + + arguments = args; + + if (!stack.empty() || !operators.empty() ) + { + logger.warn("Formula " + formula + + " has a non-empty parse stack"); + } + + return rt; + } + + /** + * Gets the list of lexical tokens using the generated lexical analyzer + * + * @return the list of tokens + * @exception FormulaException if an error occurs + */ + private ArrayList getTokens() throws FormulaException + { + ArrayList tokens = new ArrayList(); + + StringReader sr = new StringReader(formula); + Yylex lex = new Yylex(sr); + lex.setExternalSheet(externalSheet); + lex.setNameTable(nameTable); + try + { + ParseItem pi = lex.yylex(); + while (pi != null) + { + tokens.add(pi); + pi = lex.yylex(); + } + } + catch (IOException e) + { + logger.warn(e.toString()); + } + catch (Error e) + { + throw new FormulaException(FormulaException.LEXICAL_ERROR, + formula + " at char " + lex.getPos()); + } + + return tokens; + } + + /** + * Gets the formula as a string. Uses the parse tree to do this, and + * does not simply return whatever string was passed in + */ + public String getFormula() + { + if (parsedFormula == null) + { + StringBuffer sb = new StringBuffer(); + root.getString(sb); + parsedFormula = sb.toString(); + } + + return parsedFormula; + } + + /** + * Gets the bytes for the formula + * + * @return the bytes in RPN + */ + public byte[] getBytes() + { + byte[] bytes = root.getBytes(); + + if (root.isVolatile()) + { + byte[] newBytes = new byte[bytes.length + 4]; + System.arraycopy(bytes, 0, newBytes, 4, bytes.length); + newBytes[0] = Token.ATTRIBUTE.getCode(); + newBytes[1] = (byte) 0x1; + bytes = newBytes; + } + + return bytes; + } + + /** + * Handles the case when parsing a string when a token is a function + * + * @param sf the string function + * @param i the token iterator + * @param stack the parse tree stack + * @exception FormulaException if an error occurs + */ + private void handleFunction(StringFunction sf, Iterator i, + Stack stack) + throws FormulaException + { + ParseItem pi2 = parseCurrent(i); + + // If the function is unknown, then throw an error + if (sf.getFunction(settings) == Function.UNKNOWN) + { + throw new FormulaException(FormulaException.UNRECOGNIZED_FUNCTION); + } + + // First check for possible optimized functions and possible + // use of the Attribute token + if (sf.getFunction(settings) == Function.SUM && arguments == null) + { + // this is handled by an attribute + Attribute a = new Attribute(sf, settings); + a.add(pi2); + stack.push(a); + return; + } + + if (sf.getFunction(settings) == Function.IF) + { + // this is handled by an attribute + Attribute a = new Attribute(sf, settings); + + // Add in the if conditions as a var arg function in + // the correct order + VariableArgFunction vaf = new VariableArgFunction(settings); + int numargs = arguments.size(); + for (int j = 0 ; j < numargs; j++) + { + ParseItem pi3 = (ParseItem) arguments.get(j); + vaf.add(pi3); + } + + a.setIfConditions(vaf); + stack.push(a); + return; + } + + // Function cannot be optimized. See if it is a variable argument + // function or not + if (sf.getFunction(settings).getNumArgs() == 0xff) + { + // If the arg stack has not been initialized, it means + // that there was only one argument, which is the + // returned parse item + if (arguments == null) + { + int numArgs = pi2 != null? 1:0; + VariableArgFunction vaf = new VariableArgFunction + (sf.getFunction(settings), numArgs, settings); + + if (pi2 != null) + { + vaf.add(pi2); + } + + stack.push(vaf); + } + else + { + // Add the args to the function in the correct order + int numargs = arguments.size(); + VariableArgFunction vaf = new VariableArgFunction + (sf.getFunction(settings), numargs, settings); + + ParseItem[] args = new ParseItem[numargs]; + for (int j = 0 ; j < numargs; j++) + { + ParseItem pi3 = (ParseItem) arguments.pop(); + args[numargs-j-1] = pi3; + } + + for (int j = 0 ; j < args.length ; j++) + { + vaf.add(args[j]); + } + stack.push(vaf); + arguments.clear(); + arguments = null; + } + return; + } + + // Function is a standard built in function + BuiltInFunction bif = new BuiltInFunction(sf.getFunction(settings), + settings); + + int numargs = sf.getFunction(settings).getNumArgs(); + if (numargs == 1) + { + // only one item which is the returned ParseItem + bif.add(pi2); + } + else + { + if ((arguments == null && numargs != 0) || + (arguments != null && numargs != arguments.size())) + { + throw new FormulaException(FormulaException.INCORRECT_ARGUMENTS); + } + // multiple arguments so go to the arguments stack. + // Unlike the variable argument function, the args are + // stored in reverse order + for (int j = 0; j < numargs ; j++) + { + ParseItem pi3 = (ParseItem) arguments.get(j); + bif.add(pi3); + } + } + stack.push(bif); + } + + /** + * Default behaviour is to do nothing + * + * @param colAdjust the amount to add on to each relative cell reference + * @param rowAdjust the amount to add on to each relative row reference + */ + public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) + { + root.adjustRelativeCellReferences(colAdjust, rowAdjust); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param col the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + public void columnInserted(int sheetIndex, int col, boolean currentSheet) + { + root.columnInserted(sheetIndex, col, currentSheet); + } + + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param col the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + public void columnRemoved(int sheetIndex, int col, boolean currentSheet) + { + root.columnRemoved(sheetIndex, col, currentSheet); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param row the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + public void rowInserted(int sheetIndex, int row, boolean currentSheet) + { + root.rowInserted(sheetIndex, row, currentSheet); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param row the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + public void rowRemoved(int sheetIndex, int row, boolean currentSheet) + { + root.rowRemoved(sheetIndex, row, currentSheet); + } + + /** + * Handles operands by pushing them onto the stack + * + * @param o operand + * @param stack stack + */ + private void handleOperand(Operand o, Stack stack) + { + if (!(o instanceof IntegerValue)) + { + stack.push(o); + return; + } + + if (o instanceof IntegerValue) + { + IntegerValue iv = (IntegerValue) o; + if (!iv.isOutOfRange()) + { + stack.push(iv); + } + else + { + // convert to a double + DoubleValue dv = new DoubleValue(iv.getValue()); + stack.push(dv); + } + } + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * + * @return TRUE if the formula is valid import, FALSE otherwise + */ + public boolean handleImportedCellReferences() + { + root.handleImportedCellReferences(); + return root.isValid(); + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/StringFunction.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/StringFunction.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/StringFunction.java 17 Aug 2012 14:51:15 -0000 1.1 @@ -0,0 +1,71 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +import common.Logger; + +import jxl.WorkbookSettings; + +/** + * Class used to hold a function when reading it in from a string. At this + * stage it is unknown whether it is a BuiltInFunction or a VariableArgFunction + */ +class StringFunction extends StringParseItem +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(StringFunction.class); + + /** + * The function + */ + private Function function; + + /** + * The function string + */ + private String functionString; + + /** + * Constructor + * + * @param s the lexically parsed stirng + */ + StringFunction(String s) + { + functionString = s.substring(0, s.length() - 1); + } + + /** + * Accessor for the function + * + * @param ws the workbook settings + * @return the function + */ + Function getFunction(WorkbookSettings ws) + { + if (function == null) + { + function = Function.getFunction(functionString, ws); + } + return function; + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/StringOperator.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/StringOperator.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/StringOperator.java 17 Aug 2012 14:51:16 -0000 1.1 @@ -0,0 +1,160 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +import java.util.Stack; + +import common.Assert; + +/** + * Ambiguously defined operator, used as a place holder when parsing + * string formulas. At this stage it could be either + * a unary or binary operator - the string parser will deduce which and + * create the appropriate type + */ +abstract class StringOperator extends Operator +{ + /** + * Constructor + */ + protected StringOperator() + { + super(); + } + + /** + * Gets the operands for this operator from the stack. Does nothing + * here + */ + public void getOperands(Stack s) + { + Assert.verify(false); + } + + /** + * Gets the precedence for this operator. Does nothing here + * + * @return the operator precedence + */ + int getPrecedence() + { + Assert.verify(false); + return 0; + } + + /** + * Gets the token representation of this item in RPN. Does nothing here + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() + { + Assert.verify(false); + return null; + } + + /** + * Gets the string representation of this item + */ + void getString(StringBuffer buf) + { + Assert.verify(false); + } + + /** + * Default behaviour is to do nothing + * + * @param colAdjust the amount to add on to each relative cell reference + * @param rowAdjust the amount to add on to each relative row reference + */ + public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) + { + Assert.verify(false); + } + + + /** + * Default behaviour is to do nothing + * + * @param sheetIndex the sheet on which the column was inserted + * @param col the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnInserted(int sheetIndex, int col, boolean currentSheet) + { + Assert.verify(false); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param col the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnRemoved(int sheetIndex, int col, boolean currentSheet) + { + Assert.verify(false); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was inserted + * @param row the row number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowInserted(int sheetIndex, int row, boolean currentSheet) + { + Assert.verify(false); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was removed + * @param row the row number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowRemoved(int sheetIndex, int row, boolean currentSheet) + { + Assert.verify(false); + } + + /** + * Abstract method which gets the binary version of this operator + */ + abstract Operator getBinaryOperator(); + + /** + * Abstract method which gets the unary version of this operator + */ + abstract Operator getUnaryOperator(); +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/StringParseItem.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/StringParseItem.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/StringParseItem.java 17 Aug 2012 14:51:15 -0000 1.1 @@ -0,0 +1,128 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +/** + * A dummy implementation used for typing information when tokens + * are read when parsing strings. These are then stored by the parser before + * being re-stored as the appropriate RPN syntactic equivalent + */ +class StringParseItem extends ParseItem +{ + /** + * Constructor + */ + protected StringParseItem() + { + } + + /** + * Gets the string representation of this item. Does nothing here + * + * @param buf + */ + void getString(StringBuffer buf) + { + } + + /** + * Gets the token representation of this item in RPN. Does nothing here + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() + { + return new byte[0]; + } + + /** + * Default behaviour is to do nothing + * + * @param colAdjust the amount to add on to each relative cell reference + * @param rowAdjust the amount to add on to each relative row reference + */ + public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) + { + } + + /** + * Default behaviour is to do nothing + * + * @param sheetIndex the sheet on which the column was inserted + * @param col the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnInserted(int sheetIndex, int col, boolean currentSheet) + { + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param col the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnRemoved(int sheetIndex, int col, boolean currentSheet) + { + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was inserted + * @param row the row number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowInserted(int sheetIndex, int row, boolean currentSheet) + { + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was removed + * @param row the row number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowRemoved(int sheetIndex, int row, boolean currentSheet) + { + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing + */ + void handleImportedCellReferences() + { + } + +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/StringValue.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/StringValue.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/StringValue.java 17 Aug 2012 14:51:16 -0000 1.1 @@ -0,0 +1,133 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +import common.Logger; + +import jxl.WorkbookSettings; +import jxl.biff.IntegerHelper; +import jxl.biff.StringHelper; + + +/** + * A string constant operand in a formula + */ +class StringValue extends Operand implements ParsedThing +{ + /** + * The logger + */ + private final static Logger logger = Logger.getLogger(StringValue.class); + + /** + * The string value + */ + private String value; + + /** + * The workbook settings + */ + private WorkbookSettings settings; + + /** + * Constructor + */ + public StringValue(WorkbookSettings ws) + { + settings = ws; + } + + /** + * Constructor used when parsing a string formula + * + * @param s the string token, including quote marks + */ + public StringValue(String s) + { + // remove the quotes + value = s; + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) + { + int length = data[pos] & 0xff; + int consumed = 2; + + if ((data[pos+1] & 0x01) == 0) + { + value = StringHelper.getString(data, length, pos+2, settings); + consumed += length; + } + else + { + value = StringHelper.getUnicodeString(data, length, pos+2); + consumed += length * 2; + } + + return consumed; + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() + { + byte[] data = new byte[value.length() * 2 + 3]; + data[0] = Token.STRING.getCode(); + data[1] = (byte) (value.length()); + data[2] = 0x01; + StringHelper.getUnicodeBytes(value, data, 3); + + return data; + } + + /** + * Abstract method implementation to get the string equivalent of this + * token + * + * @param buf the string to append to + */ + public void getString(StringBuffer buf) + { + buf.append("\""); + buf.append(value); + buf.append("\""); + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing + */ + void handleImportedCellReferences() + { + } + +} + Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/SubExpression.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/SubExpression.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/SubExpression.java 17 Aug 2012 14:51:15 -0000 1.1 @@ -0,0 +1,116 @@ +/********************************************************************* +* +* Copyright (C) 2007 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +import java.util.Stack; + +import jxl.biff.IntegerHelper; + +/** + * Base class for those tokens which encapsulate a subexpression + */ +abstract class SubExpression extends Operand implements ParsedThing +{ + /** + * The number of bytes in the subexpression + */ + private int length; + + /** + * The sub expression + */ + private ParseItem[] subExpression; + + /** + * Constructor + */ + protected SubExpression() + { + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) + { + length = IntegerHelper.getInt(data[pos], data[pos+1]); + return 2; + } + + /** + * Gets the operands for this operator from the stack + */ + public void getOperands(Stack s) + { + } + + /** + * Gets the token representation of this item in RPN. The Attribute + * token is a special case, which overrides anything useful we could do + * in the base class + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() + { + return null; + } + + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() + { + return 5; + } + + /** + * Accessor for the length + * + * @return the length of the subexpression + */ + public int getLength() + { + return length; + } + + protected final void setLength(int l) + { + length = l; + } + + public void setSubExpression(ParseItem[] pi) + { + subExpression = pi; + } + + protected ParseItem[] getSubExpression() + { + return subExpression; + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/Subtract.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/Subtract.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/Subtract.java 17 Aug 2012 14:51:15 -0000 1.1 @@ -0,0 +1,59 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +/** + * A cell reference in a formula + */ +class Subtract extends BinaryOperator implements ParsedThing +{ + /** + * Constructor + */ + public Subtract() + { + } + + public String getSymbol() + { + return "-"; + } + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + Token getToken() + { + return Token.SUBTRACT; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() + { + return 4; + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/Token.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/Token.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/Token.java 17 Aug 2012 14:51:15 -0000 1.1 @@ -0,0 +1,198 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +import java.util.HashMap; + +/** + * An enumeration detailing the Excel parsed tokens + * A particular token may be associated with more than one token code + */ +class Token +{ + /** + * The array of values which apply to this token + */ + public final int[] value; + + /** + * All available tokens, keyed on value + */ + private static HashMap tokens = new HashMap(20); + + /** + * Constructor + * Sets the token value and adds this token to the array of all token + * + * @param v the biff code for the token + */ + private Token(int v) + { + value = new int[] {v}; + + tokens.put(new Integer(v), this); + } + + /** + * Constructor + * Sets the token value and adds this token to the array of all token + * + * @param v the biff code for the token + */ + private Token(int v1, int v2) + { + value = new int[] {v1, v2}; + + tokens.put(new Integer(v1), this); + tokens.put(new Integer(v2), this); + } + + /** + * Constructor + * Sets the token value and adds this token to the array of all token + * + * @param v the biff code for the token + */ + private Token(int v1, int v2, int v3) + { + value = new int[] {v1, v2, v3}; + + tokens.put(new Integer(v1), this); + tokens.put(new Integer(v2), this); + tokens.put(new Integer(v3), this); + } + + /** + * Constructor + * Sets the token value and adds this token to the array of all token + * + * @param v the biff code for the token + */ + private Token(int v1, int v2, int v3, int v4) + { + value = new int[] {v1, v2, v3, v4}; + + tokens.put(new Integer(v1), this); + tokens.put(new Integer(v2), this); + tokens.put(new Integer(v3), this); + tokens.put(new Integer(v4), this); + } + + /** + * Constructor + * Sets the token value and adds this token to the array of all token + * + * @param v the biff code for the token + */ + private Token(int v1, int v2, int v3, int v4, int v5) + { + value = new int[] {v1, v2, v3, v4, v5}; + + tokens.put(new Integer(v1), this); + tokens.put(new Integer(v2), this); + tokens.put(new Integer(v3), this); + tokens.put(new Integer(v4), this); + tokens.put(new Integer(v5), this); + } + + /** + * Gets the token code for the specified token + * + * @return the token code. This is the first item in the array + */ + public byte getCode() + { + return (byte) value[0]; + } + + /** + * Gets an alternative token code for the specified token + * Used for certain types of volatile function + * + * @return the token code + */ + public byte getCode2() + { + return (byte) (value.length > 0 ? value[1] : value[0]); + } + + /** + * Gets the type object from its integer value + */ + public static Token getToken(int v) + { + Token t = (Token) tokens.get(new Integer(v)); + + return t != null ? t : UNKNOWN; + } + + // Operands + public static final Token REF = new Token(0x44, 0x24, 0x64); + public static final Token REF3D = new Token(0x5a, 0x3a, 0x7a); + public static final Token MISSING_ARG = new Token(0x16); + public static final Token STRING = new Token(0x17); + public static final Token ERR = new Token(0x1c); + public static final Token BOOL = new Token(0x1d); + public static final Token INTEGER = new Token(0x1e); + public static final Token DOUBLE = new Token(0x1f); + public static final Token REFERR = new Token(0x2a, 0x4a, 0x6a); + public static final Token REFV = new Token(0x2c, 0x4c, 0x6c); + public static final Token AREAV = new Token(0x2d, 0x4d, 0x6d); + public static final Token MEM_AREA = new Token(0x26, 0x46, 0x66); + public static final Token AREA = new Token(0x25, 0x65, 0x45); + public static final Token NAMED_RANGE = new Token(0x23, 0x43, 0x63); + //need 0x23 for data validation references + public static final Token NAME = new Token(0x39, 0x59); + public static final Token AREA3D = new Token(0x3b, 0x5b); + + // Unary Operators + public static final Token UNARY_PLUS = new Token(0x12); + public static final Token UNARY_MINUS = new Token(0x13); + public static final Token PERCENT = new Token(0x14); + public static final Token PARENTHESIS = new Token(0x15); + + // Binary Operators + public static final Token ADD = new Token(0x3); + public static final Token SUBTRACT = new Token(0x4); + public static final Token MULTIPLY = new Token(0x5); + public static final Token DIVIDE = new Token(0x6); + public static final Token POWER = new Token(0x7); + public static final Token CONCAT = new Token(0x8); + public static final Token LESS_THAN = new Token(0x9); + public static final Token LESS_EQUAL = new Token(0xa); + public static final Token EQUAL = new Token(0xb); + public static final Token GREATER_EQUAL = new Token(0xc); + public static final Token GREATER_THAN = new Token(0xd); + public static final Token NOT_EQUAL = new Token(0xe); + public static final Token UNION = new Token(0x10); + public static final Token RANGE = new Token(0x11); + + // Functions + public static final Token FUNCTION = new Token(0x41, 0x21, 0x61); + public static final Token FUNCTIONVARARG = new Token(0x42, 0x22, 0x62); + + // Control + public static final Token ATTRIBUTE = new Token(0x19); + public static final Token MEM_FUNC = new Token(0x29, 0x49, 0x69); + + // Unknown token + public static final Token UNKNOWN = new Token(0xffff); +} + Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/TokenFormulaParser.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/TokenFormulaParser.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/TokenFormulaParser.java 17 Aug 2012 14:51:15 -0000 1.1 @@ -0,0 +1,559 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +import java.util.Stack; + +import common.Assert; +import common.Logger; + +import jxl.Cell; +import jxl.WorkbookSettings; +import jxl.biff.WorkbookMethods; + +/** + * Parses the excel ptgs into a parse tree + */ +class TokenFormulaParser implements Parser +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(TokenFormulaParser.class); + + /** + * The Excel ptgs + */ + private byte[] tokenData; + + /** + * The cell containing the formula. This is used in order to determine + * relative cell values + */ + private Cell relativeTo; + + /** + * The current position within the array + */ + private int pos; + + /** + * The parse tree + */ + private ParseItem root; + + /** + * The hash table of items that have been parsed + */ + private Stack tokenStack; + + /** + * A reference to the workbook which holds the external sheet + * information + */ + private ExternalSheet workbook; + + /** + * A reference to the name table + */ + private WorkbookMethods nameTable; + + /** + * The workbook settings + */ + private WorkbookSettings settings; + + /** + * Constructor + */ + public TokenFormulaParser(byte[] data, + Cell c, + ExternalSheet es, + WorkbookMethods nt, + WorkbookSettings ws) + { + tokenData = data; + pos = 0; + relativeTo = c; + workbook = es; + nameTable = nt; + tokenStack = new Stack(); + settings = ws; + + Assert.verify(nameTable != null); + } + + /** + * Parses the sublist of tokens. In most cases this will equate to + * the full list + * + * @exception FormulaException + */ + public void parse() throws FormulaException + { + parseSubExpression(tokenData.length); + + // Finally, there should be one thing left on the stack. Get that + // and add it to the root node + root = (ParseItem) tokenStack.pop(); + + Assert.verify(tokenStack.empty()); + + } + + /** + * Parses the sublist of tokens. In most cases this will equate to + * the full list + * + * @param len the length of the subexpression to parse + * @exception FormulaException + */ + private void parseSubExpression(int len) throws FormulaException + { + int tokenVal = 0; + Token t = null; + + // Indicates that we are parsing the incredibly complicated and + // hacky if construct that MS saw fit to include, the gits + Stack ifStack = new Stack(); + + // The end position of the sub-expression + int endpos = pos + len; + + while (pos < endpos) + { + tokenVal = tokenData[pos]; + pos++; + + t = Token.getToken(tokenVal); + + if (t == Token.UNKNOWN) + { + throw new FormulaException + (FormulaException.UNRECOGNIZED_TOKEN, tokenVal); + } + + Assert.verify(t != Token.UNKNOWN); + + // Operands + if (t == Token.REF) + { + CellReference cr = new CellReference(relativeTo); + pos += cr.read(tokenData, pos); + tokenStack.push(cr); + } + else if (t == Token.REFERR) + { + CellReferenceError cr = new CellReferenceError(); + pos += cr.read(tokenData, pos); + tokenStack.push(cr); + } + else if (t == Token.ERR) + { + ErrorConstant ec = new ErrorConstant(); + pos += ec.read(tokenData, pos); + tokenStack.push(ec); + } + else if (t == Token.REFV) + { + SharedFormulaCellReference cr = + new SharedFormulaCellReference(relativeTo); + pos += cr.read(tokenData, pos); + tokenStack.push(cr); + } + else if (t == Token.REF3D) + { + CellReference3d cr = new CellReference3d(relativeTo, workbook); + pos += cr.read(tokenData, pos); + tokenStack.push(cr); + } + else if (t == Token.AREA) + { + Area a = new Area(); + pos += a.read(tokenData, pos); + tokenStack.push(a); + } + else if (t == Token.AREAV) + { + SharedFormulaArea a = new SharedFormulaArea(relativeTo); + pos += a.read(tokenData, pos); + tokenStack.push(a); + } + else if (t == Token.AREA3D) + { + Area3d a = new Area3d(workbook); + pos += a.read(tokenData, pos); + tokenStack.push(a); + } + else if (t == Token.NAME) + { + Name n = new Name(); + pos += n.read(tokenData, pos); + tokenStack.push(n); + } + else if (t == Token.NAMED_RANGE) + { + NameRange nr = new NameRange(nameTable); + pos += nr.read(tokenData, pos); + tokenStack.push(nr); + } + else if (t == Token.INTEGER) + { + IntegerValue i = new IntegerValue(); + pos += i.read(tokenData, pos); + tokenStack.push(i); + } + else if (t == Token.DOUBLE) + { + DoubleValue d = new DoubleValue(); + pos += d.read(tokenData, pos); + tokenStack.push(d); + } + else if (t == Token.BOOL) + { + BooleanValue bv = new BooleanValue(); + pos += bv.read(tokenData, pos); + tokenStack.push(bv); + } + else if (t == Token.STRING) + { + StringValue sv = new StringValue(settings); + pos += sv.read(tokenData, pos); + tokenStack.push(sv); + } + else if (t == Token.MISSING_ARG) + { + MissingArg ma = new MissingArg(); + pos += ma.read(tokenData, pos); + tokenStack.push(ma); + } + + // Unary Operators + else if (t == Token.UNARY_PLUS) + { + UnaryPlus up = new UnaryPlus(); + pos += up.read(tokenData, pos); + addOperator(up); + } + else if (t == Token.UNARY_MINUS) + { + UnaryMinus um = new UnaryMinus(); + pos += um.read(tokenData, pos); + addOperator(um); + } + else if (t == Token.PERCENT) + { + Percent p = new Percent(); + pos += p.read(tokenData, pos); + addOperator(p); + } + + // Binary Operators + else if (t == Token.SUBTRACT) + { + Subtract s = new Subtract(); + pos += s.read(tokenData, pos); + addOperator(s); + } + else if (t == Token.ADD) + { + Add s = new Add(); + pos += s.read(tokenData, pos); + addOperator(s); + } + else if (t == Token.MULTIPLY) + { + Multiply s = new Multiply(); + pos += s.read(tokenData, pos); + addOperator(s); + } + else if (t == Token.DIVIDE) + { + Divide s = new Divide(); + pos += s.read(tokenData, pos); + addOperator(s); + } + else if (t == Token.CONCAT) + { + Concatenate c = new Concatenate(); + pos += c.read(tokenData, pos); + addOperator(c); + } + else if (t == Token.POWER) + { + Power p = new Power(); + pos += p.read(tokenData, pos); + addOperator(p); + } + else if (t == Token.LESS_THAN) + { + LessThan lt = new LessThan(); + pos += lt.read(tokenData, pos); + addOperator(lt); + } + else if (t == Token.LESS_EQUAL) + { + LessEqual lte = new LessEqual(); + pos += lte.read(tokenData, pos); + addOperator(lte); + } + else if (t == Token.GREATER_THAN) + { + GreaterThan gt = new GreaterThan(); + pos += gt.read(tokenData, pos); + addOperator(gt); + } + else if (t == Token.GREATER_EQUAL) + { + GreaterEqual gte = new GreaterEqual(); + pos += gte.read(tokenData, pos); + addOperator(gte); + } + else if (t == Token.NOT_EQUAL) + { + NotEqual ne = new NotEqual(); + pos += ne.read(tokenData, pos); + addOperator(ne); + } + else if (t == Token.EQUAL) + { + Equal e = new Equal(); + pos += e.read(tokenData, pos); + addOperator(e); + } + else if (t == Token.PARENTHESIS) + { + Parenthesis p = new Parenthesis(); + pos += p.read(tokenData, pos); + addOperator(p); + } + + // Functions + else if (t == Token.ATTRIBUTE) + { + Attribute a = new Attribute(settings); + pos += a.read(tokenData, pos); + + if (a.isSum()) + { + addOperator(a); + } + else if (a.isIf()) + { + // Add it to a special stack for ifs + ifStack.push(a); + } + } + else if (t == Token.FUNCTION) + { + BuiltInFunction bif = new BuiltInFunction(settings); + pos += bif.read(tokenData, pos); + + addOperator(bif); + } + else if (t == Token.FUNCTIONVARARG) + { + VariableArgFunction vaf = new VariableArgFunction(settings); + pos += vaf.read(tokenData, pos); + + if (vaf.getFunction() != Function.ATTRIBUTE) + { + addOperator(vaf); + } + else + { + // This is part of an IF function. Get the operands, but then + // add it to the top of the if stack + vaf.getOperands(tokenStack); + + Attribute ifattr = null; + if (ifStack.empty()) + { + ifattr = new Attribute(settings); + } + else + { + ifattr = (Attribute) ifStack.pop(); + } + + ifattr.setIfConditions(vaf); + tokenStack.push(ifattr); + } + } + + // Other things + else if (t == Token.MEM_FUNC) + { + MemFunc memFunc = new MemFunc(); + handleMemoryFunction(memFunc); + } + else if (t == Token.MEM_AREA) + { + MemArea memArea = new MemArea(); + handleMemoryFunction(memArea); + } + } + } + + /** + * Handles a memory function + */ + private void handleMemoryFunction(SubExpression subxp) + throws FormulaException + { + pos += subxp.read(tokenData, pos); + + // Create new tokenStack for the sub expression + Stack oldStack = tokenStack; + tokenStack = new Stack(); + + parseSubExpression(subxp.getLength()); + + ParseItem[] subexpr = new ParseItem[tokenStack.size()]; + int i = 0; + while (!tokenStack.isEmpty()) + { + subexpr[i] = (ParseItem) tokenStack.pop(); + i++; + } + + subxp.setSubExpression(subexpr); + + tokenStack = oldStack; + tokenStack.push(subxp); + } + + /** + * Adds the specified operator to the parse tree, taking operands off + * the stack as appropriate + */ + private void addOperator(Operator o) + { + // Get the operands off the stack + o.getOperands(tokenStack); + + // Add this operator onto the stack + tokenStack.push(o); + } + + /** + * Gets the formula as a string + */ + public String getFormula() + { + StringBuffer sb = new StringBuffer(); + root.getString(sb); + return sb.toString(); + } + + /** + * Adjusts all the relative cell references in this formula by the + * amount specified. Used when copying formulas + * + * @param colAdjust the amount to add on to each relative cell reference + * @param rowAdjust the amount to add on to each relative row reference + */ + public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) + { + root.adjustRelativeCellReferences(colAdjust, rowAdjust); + } + + /** + * Gets the bytes for the formula. This takes into account any + * token mapping necessary because of shared formulas + * + * @return the bytes in RPN + */ + public byte[] getBytes() + { + return root.getBytes(); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param col the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + public void columnInserted(int sheetIndex, int col, boolean currentSheet) + { + root.columnInserted(sheetIndex, col, currentSheet); + } + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param col the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + public void columnRemoved(int sheetIndex, int col, boolean currentSheet) + { + root.columnRemoved(sheetIndex, col, currentSheet); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param row the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + public void rowInserted(int sheetIndex, int row, boolean currentSheet) + { + root.rowInserted(sheetIndex, row, currentSheet); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param row the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + public void rowRemoved(int sheetIndex, int row, boolean currentSheet) + { + root.rowRemoved(sheetIndex, row, currentSheet); + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * + * @return TRUE if the formula is valid import, FALSE otherwise + */ + public boolean handleImportedCellReferences() + { + root.handleImportedCellReferences(); + return root.isValid(); + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/UnaryMinus.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/UnaryMinus.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/UnaryMinus.java 17 Aug 2012 14:51:14 -0000 1.1 @@ -0,0 +1,68 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +/** + * A cell reference in a formula + */ +class UnaryMinus extends UnaryOperator implements ParsedThing +{ + /** + * Constructor + */ + public UnaryMinus() + { + } + + public String getSymbol() + { + return "-"; + } + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + Token getToken() + { + return Token.UNARY_MINUS; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() + { + return 2; + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing, as operators don't have cell references + */ + void handleImportedCellReferences() + { + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/UnaryOperator.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/UnaryOperator.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/UnaryOperator.java 17 Aug 2012 14:51:15 -0000 1.1 @@ -0,0 +1,192 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +import java.util.Stack; + +/** + * A cell reference in a formula + */ +abstract class UnaryOperator extends Operator implements ParsedThing +{ + /** + * Constructor + */ + public UnaryOperator() + { + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + */ + public int read(byte[] data, int pos) + { + return 0; + } + + /** + * Gets the operands for this operator from the stack + */ + public void getOperands(Stack s) + { + ParseItem o1 = (ParseItem) s.pop(); + + add(o1); + } + + /** + * Gets the string + * + * @param buf + */ + public void getString(StringBuffer buf) + { + ParseItem[] operands = getOperands(); + buf.append(getSymbol()); + operands[0].getString(buf); + } + + /** + * Adjusts all the relative cell references in this formula by the + * amount specified. Used when copying formulas + * + * @param colAdjust the amount to add on to each relative cell reference + * @param rowAdjust the amount to add on to each relative row reference + */ + public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) + { + ParseItem[] operands = getOperands(); + operands[0].adjustRelativeCellReferences(colAdjust, rowAdjust); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param col the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnInserted(int sheetIndex, int col, boolean currentSheet) + { + ParseItem[] operands = getOperands(); + operands[0].columnInserted(sheetIndex, col, currentSheet); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param col the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnRemoved(int sheetIndex, int col, boolean currentSheet) + { + ParseItem[] operands = getOperands(); + operands[0].columnRemoved(sheetIndex, col, currentSheet); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was inserted + * @param row the row number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowInserted(int sheetIndex, int row, boolean currentSheet) + { + ParseItem[] operands = getOperands(); + operands[0].rowInserted(sheetIndex, row, currentSheet); + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was removed + * @param row the row number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowRemoved(int sheetIndex, int row, boolean currentSheet) + { + ParseItem[] operands = getOperands(); + operands[0].rowRemoved(sheetIndex, row, currentSheet); + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() + { + // Get the data for the operands + ParseItem[] operands = getOperands(); + byte[] data = operands[0].getBytes(); + + // Add on the operator byte + byte[] newdata = new byte[data.length + 1]; + System.arraycopy(data, 0, newdata, 0, data.length); + newdata[data.length] = getToken().getCode(); + + return newdata; + } + + /** + * Abstract method which gets the binary operator string symbol + * + * @return the string symbol for this token + */ + abstract String getSymbol(); + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + abstract Token getToken(); + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing, as operators don't have cell references + */ + void handleImportedCellReferences() + { + ParseItem[] operands = getOperands(); + operands[0].handleImportedCellReferences(); + } + + +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/UnaryPlus.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/UnaryPlus.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/UnaryPlus.java 17 Aug 2012 14:51:17 -0000 1.1 @@ -0,0 +1,68 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +/** + * A cell reference in a formula + */ +class UnaryPlus extends UnaryOperator implements ParsedThing +{ + /** + * Constructor + */ + public UnaryPlus() + { + } + + public String getSymbol() + { + return "+"; + } + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + Token getToken() + { + return Token.UNARY_PLUS; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() + { + return 2; + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing, as operators don't have cell references + */ + void handleImportedCellReferences() + { + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/Union.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/Union.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/Union.java 17 Aug 2012 14:51:16 -0000 1.1 @@ -0,0 +1,59 @@ +/********************************************************************* +* +* Copyright (C) 2005 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +/** + * A union of two cell ranges - the "," operator + */ +class Union extends BinaryOperator implements ParsedThing +{ + /** + * Constructor + */ + public Union() + { + } + + public String getSymbol() + { + return ","; + } + + /** + * Abstract method which gets the token for this operator + * + * @return the string symbol for this token + */ + Token getToken() + { + return Token.UNION; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() + { + return 4; + } +} Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/VariableArgFunction.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/VariableArgFunction.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/VariableArgFunction.java 17 Aug 2012 14:51:15 -0000 1.1 @@ -0,0 +1,348 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +import java.util.Stack; + +import common.Logger; + +import jxl.WorkbookSettings; +import jxl.biff.IntegerHelper; + +/** + * A built in function in a formula. These functions take a variable + * number of arguments, such as a range (eg. SUM etc) + */ +class VariableArgFunction extends Operator implements ParsedThing +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(VariableArgFunction.class); + + /** + * The function + */ + private Function function; + + /** + * The number of arguments + */ + private int arguments; + + /** + * Flag which indicates whether this was initialized from the client + * api or from an excel sheet + */ + private boolean readFromSheet; + + /** + * The workbooks settings + */ + private WorkbookSettings settings; + + /** + * Constructor + */ + public VariableArgFunction(WorkbookSettings ws) + { + readFromSheet = true; + settings = ws; + } + + /** + * Constructor used when parsing a function from a string + * + * @param f the function + * @param a the number of arguments + */ + public VariableArgFunction(Function f, int a, WorkbookSettings ws) + { + function = f; + arguments = a; + readFromSheet = false; + settings = ws; + } + + /** + * Reads the ptg data from the array starting at the specified position + * + * @param data the RPN array + * @param pos the current position in the array, excluding the ptg identifier + * @return the number of bytes read + * @exception FormulaException + */ + public int read(byte[] data, int pos) throws FormulaException + { + arguments = data[pos]; + int index = IntegerHelper.getInt(data[pos+1], data[pos+2]); + function = Function.getFunction(index); + + if (function == Function.UNKNOWN) + { + throw new FormulaException(FormulaException.UNRECOGNIZED_FUNCTION, + index); + } + + return 3; + } + + /** + * Gets the operands for this operator from the stack + */ + public void getOperands(Stack s) + { + // parameters are in the correct order, god damn them + ParseItem[] items = new ParseItem[arguments]; + + for (int i = arguments - 1; i >= 0 ; i--) + { + ParseItem pi = (ParseItem) s.pop(); + + items[i] = pi; + } + + for (int i = 0 ; i < arguments; i++) + { + add(items[i]); + } + } + + public void getString(StringBuffer buf) + { + buf.append(function.getName(settings)); + buf.append('('); + + if (arguments > 0) + { + ParseItem[] operands = getOperands(); + if (readFromSheet) + { + // arguments are in the same order they were specified + operands[0].getString(buf); + + for (int i = 1; i < arguments; i++) + { + buf.append(','); + operands[i].getString(buf); + } + } + else + { + // arguments are stored in the reverse order to which they + // were specified, so iterate through them backwards + operands[arguments - 1].getString(buf); + + for (int i = arguments - 2; i >= 0 ; i--) + { + buf.append(','); + operands[i].getString(buf); + } + } + } + + buf.append(')'); + } + + /** + * Adjusts all the relative cell references in this formula by the + * amount specified. Used when copying formulas + * + * @param colAdjust the amount to add on to each relative cell reference + * @param rowAdjust the amount to add on to each relative row reference + */ + public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) + { + ParseItem[] operands = getOperands(); + + for (int i = 0 ; i < operands.length ; i++) + { + operands[i].adjustRelativeCellReferences(colAdjust, rowAdjust); + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was inserted + * @param col the column number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnInserted(int sheetIndex, int col, boolean currentSheet) + { + ParseItem[] operands = getOperands(); + for (int i = 0 ; i < operands.length ; i++) + { + operands[i].columnInserted(sheetIndex, col, currentSheet); + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the column was removed + * @param col the column number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void columnRemoved(int sheetIndex, int col, boolean currentSheet) + { + ParseItem[] operands = getOperands(); + for (int i = 0 ; i < operands.length ; i++) + { + operands[i].columnRemoved(sheetIndex, col, currentSheet); + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was inserted + * @param row the row number which was inserted + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowInserted(int sheetIndex, int row, boolean currentSheet) + { + ParseItem[] operands = getOperands(); + for (int i = 0 ; i < operands.length ; i++) + { + operands[i].rowInserted(sheetIndex, row, currentSheet); + } + } + + /** + * Called when a column is inserted on the specified sheet. Tells + * the formula parser to update all of its cell references beyond this + * column + * + * @param sheetIndex the sheet on which the row was removed + * @param row the row number which was removed + * @param currentSheet TRUE if this formula is on the sheet in which the + * column was inserted, FALSE otherwise + */ + void rowRemoved(int sheetIndex, int row, boolean currentSheet) + { + ParseItem[] operands = getOperands(); + for (int i = 0 ; i < operands.length ; i++) + { + operands[i].rowRemoved(sheetIndex, row, currentSheet); + } + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * Does nothing, as operators don't have cell references + */ + void handleImportedCellReferences() + { + ParseItem[] operands = getOperands(); + for (int i = 0 ; i < operands.length ; i++) + { + operands[i].handleImportedCellReferences(); + } + } + + /** + * Gets the underlying function + */ + Function getFunction() + { + return function; + } + + /** + * Gets the token representation of this item in RPN + * + * @return the bytes applicable to this formula + */ + byte[] getBytes() + { + handleSpecialCases(); + + // Get the data for the operands - in the correct order + ParseItem[] operands = getOperands(); + byte[] data = new byte[0]; + + for (int i = 0 ; i < operands.length ; i++) + { + byte[] opdata = operands[i].getBytes(); + + // Grow the array + byte[] newdata = new byte[data.length + opdata.length]; + System.arraycopy(data, 0, newdata, 0, data.length); + System.arraycopy(opdata, 0, newdata, data.length, opdata.length); + data = newdata; + } + + // Add on the operator byte + byte[] newdata = new byte[data.length + 4]; + System.arraycopy(data, 0, newdata, 0, data.length); + newdata[data.length] = !useAlternateCode() ? + Token.FUNCTIONVARARG.getCode() : Token.FUNCTIONVARARG.getCode2() ; + newdata[data.length+1] = (byte) arguments; + IntegerHelper.getTwoBytes(function.getCode(), newdata, data.length+2); + + return newdata; + } + + /** + * Gets the precedence for this operator. Operator precedents run from + * 1 to 5, one being the highest, 5 being the lowest + * + * @return the operator precedence + */ + int getPrecedence() + { + return 3; + } + + /** + * Handles functions which form a special case + */ + private void handleSpecialCases() + { + // Handle the array functions. Tell all the Area records to + // use their alternative token code + if (function == Function.SUMPRODUCT) + { + // Get the data for the operands - in reverse order + ParseItem[] operands = getOperands(); + + for (int i = operands.length - 1 ; i >= 0 ; i--) + { + if (operands[i] instanceof Area) + { + operands[i].setAlternateCode(); + } + } + } + } +} + + Index: 3rdParty_sources/jexcelapi/jxl/biff/formula/Yylex.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/biff/formula/Yylex.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/biff/formula/Yylex.java 17 Aug 2012 14:51:16 -0000 1.1 @@ -0,0 +1,813 @@ +/* The following code was generated by JFlex 1.4.1 on 25/11/08 00:40 */ + +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.biff.formula; + +/** + * This file is generated by JLex. Do not alter the contents of this file + * because changes will be overridden + */ + +import jxl.biff.WorkbookMethods; + + +/** + * This class is a scanner generated by + * JFlex 1.4.1 + * on 25/11/08 00:40 from the specification file + * xlformula.flex + */ +class Yylex { + + /** This character denotes the end of file */ + public static final int YYEOF = -1; + + /** initial size of the lookahead buffer */ + private static final int ZZ_BUFFERSIZE = 16384; + + /** lexical states */ + public static final int YYSTRING = 1; + public static final int YYINITIAL = 0; + + /** + * Translates characters to character classes + */ + private static final String ZZ_CMAP_PACKED = + "\10\0\3\25\25\0\1\25\1\24\1\21\1\26\1\10\2\0\1\22"+ + "\1\5\1\6\1\41\1\37\1\4\1\40\1\7\1\33\1\34\11\2"+ + "\1\3\1\0\1\44\1\43\1\42\1\36\1\0\1\16\2\1\1\30"+ + "\1\14\1\15\2\1\1\31\2\1\1\17\1\35\1\27\3\1\1\12"+ + "\1\20\1\11\1\13\1\32\4\1\4\0\1\23\1\0\32\1\uff85\0"; + + /** + * Translates characters to character classes + */ + private static final char [] ZZ_CMAP = zzUnpackCMap(ZZ_CMAP_PACKED); + + /** + * Translates DFA states to action switch labels. + */ + private static final int [] ZZ_ACTION = zzUnpackAction(); + + private static final String ZZ_ACTION_PACKED_0 = + "\1\0\1\1\1\2\1\3\1\4\1\5\1\6\1\7"+ + "\1\0\2\2\1\10\1\0\1\11\1\0\1\12\1\13"+ + "\1\14\1\15\1\16\1\17\1\20\1\1\1\21\1\2"+ + "\1\22\1\0\1\23\1\0\1\2\3\0\2\2\5\0"+ + "\1\24\1\25\1\26\1\2\1\0\1\27\1\0\1\22"+ + "\2\0\1\30\1\0\2\2\10\0\1\27\1\0\1\31"+ + "\1\0\1\32\10\0\1\33\2\0\1\31\2\0\1\34"+ + "\4\0\1\35\3\0\1\35\1\0\1\36\1\0"; + + private static int [] zzUnpackAction() { + int [] result = new int[94]; + int offset = 0; + offset = zzUnpackAction(ZZ_ACTION_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackAction(String packed, int offset, int [] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int count = packed.charAt(i++); + int value = packed.charAt(i++); + do result[j++] = value; while (--count > 0); + } + return j; + } + + + /** + * Translates a state to a row index in the transition table + */ + private static final int [] ZZ_ROWMAP = zzUnpackRowMap(); + + private static final String ZZ_ROWMAP_PACKED_0 = + "\0\0\0\45\0\112\0\157\0\224\0\224\0\224\0\224"+ + "\0\271\0\336\0\u0103\0\224\0\u0128\0\224\0\u014d\0\224"+ + "\0\224\0\224\0\224\0\u0172\0\224\0\u0197\0\u01bc\0\224"+ + "\0\u01e1\0\u0206\0\u022b\0\224\0\u0250\0\u0275\0\u029a\0\u02bf"+ + "\0\u02e4\0\u0309\0\u032e\0\u0353\0\u0378\0\u039d\0\u03c2\0\u03e7"+ + "\0\224\0\224\0\224\0\u040c\0\u0431\0\u0456\0\u047b\0\u04a0"+ + "\0\u04c5\0\u04ea\0\u02bf\0\u050f\0\u0534\0\u0559\0\u057e\0\u05a3"+ + "\0\u05c8\0\u05ed\0\u0612\0\u0637\0\u065c\0\u0681\0\224\0\u06a6"+ + "\0\u06cb\0\u06cb\0\u040c\0\u06f0\0\u0715\0\u073a\0\u075f\0\u0784"+ + "\0\u07a9\0\u07ce\0\u07f3\0\u0818\0\u0818\0\u083d\0\u0862\0\u0887"+ + "\0\u08ac\0\224\0\u08d1\0\u08f6\0\u091b\0\u0940\0\u0965\0\u098a"+ + "\0\u09af\0\u09d4\0\224\0\u09f9\0\u0a1e\0\u0a1e"; + + private static int [] zzUnpackRowMap() { + int [] result = new int[94]; + int offset = 0; + offset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackRowMap(String packed, int offset, int [] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int high = packed.charAt(i++) << 16; + result[j++] = high | packed.charAt(i++); + } + return j; + } + + /** + * The transition table of the DFA + */ + private static final int [] ZZ_TRANS = zzUnpackTrans(); + + private static final String ZZ_TRANS_PACKED_0 = + "\1\0\1\3\1\4\1\5\1\6\1\7\1\10\1\0"+ + "\1\11\1\12\3\3\1\13\3\3\1\14\1\15\2\0"+ + "\1\16\1\17\4\3\1\20\1\4\1\3\1\0\1\21"+ + "\1\22\1\23\1\24\1\25\1\26\21\27\1\30\23\27"+ + "\1\0\1\31\1\32\1\33\1\0\1\34\2\0\1\35"+ + "\10\31\2\0\1\36\1\37\2\0\4\31\1\0\1\32"+ + "\1\31\11\0\1\4\4\0\1\40\24\0\1\4\56\0"+ + "\1\41\7\0\10\41\6\0\4\41\2\0\1\41\10\0"+ + "\1\31\1\32\1\33\1\0\1\34\2\0\1\35\1\31"+ + "\1\42\6\31\2\0\1\36\1\37\2\0\4\31\1\0"+ + "\1\32\1\31\10\0\1\31\1\32\1\33\1\0\1\34"+ + "\2\0\1\35\5\31\1\43\2\31\2\0\1\36\1\37"+ + "\2\0\4\31\1\0\1\32\1\31\7\0\22\15\1\44"+ + "\22\15\12\0\1\45\14\0\1\46\1\47\1\0\1\50"+ + "\55\0\1\51\43\0\1\52\1\53\1\0\21\27\1\0"+ + "\23\27\1\0\1\54\1\32\1\33\1\0\1\34\2\0"+ + "\1\35\10\54\2\0\1\36\1\37\2\0\4\54\1\0"+ + "\1\32\1\54\10\0\1\36\1\32\1\55\5\0\10\36"+ + "\2\0\1\36\3\0\4\36\1\0\1\32\1\36\10\0"+ + "\1\56\6\0\1\57\10\56\6\0\4\56\2\0\1\56"+ + "\11\0\1\60\31\0\1\60\11\0\2\36\6\0\10\36"+ + "\2\0\1\36\3\0\4\36\1\0\2\36\10\0\1\61"+ + "\6\0\1\62\10\61\6\0\4\61\2\0\1\61\11\0"+ + "\1\63\31\0\1\63\11\0\1\64\1\60\1\33\4\0"+ + "\1\35\10\64\6\0\4\64\1\0\1\60\1\64\10\0"+ + "\1\54\1\32\1\33\1\0\1\34\2\0\1\35\2\54"+ + "\1\65\5\54\2\0\1\36\1\37\2\0\4\54\1\0"+ + "\1\32\1\54\10\0\1\54\1\32\1\33\1\0\1\34"+ + "\2\0\1\35\6\54\1\66\1\54\2\0\1\36\1\37"+ + "\2\0\4\54\1\0\1\32\1\54\33\0\1\67\34\0"+ + "\1\70\43\0\1\71\2\0\1\72\57\0\1\73\31\0"+ + "\1\74\27\0\1\54\1\36\2\0\1\34\3\0\10\54"+ + "\2\0\1\36\1\37\2\0\4\54\1\0\1\36\1\54"+ + "\10\0\1\75\6\0\1\76\10\75\6\0\4\75\2\0"+ + "\1\75\10\0\1\77\7\0\10\77\6\0\4\77\2\0"+ + "\1\77\10\0\1\56\7\0\10\56\6\0\4\56\2\0"+ + "\1\56\11\0\1\60\1\55\30\0\1\60\11\0\1\100"+ + "\1\101\5\0\1\102\10\100\6\0\4\100\1\0\1\101"+ + "\1\100\10\0\1\61\7\0\10\61\6\0\4\61\2\0"+ + "\1\61\11\0\1\60\1\33\4\0\1\35\23\0\1\60"+ + "\11\0\1\54\1\36\2\0\1\34\3\0\3\54\1\103"+ + "\4\54\2\0\1\36\1\37\2\0\4\54\1\0\1\36"+ + "\1\54\10\0\1\54\1\36\2\0\1\34\3\0\7\54"+ + "\1\65\2\0\1\36\1\37\2\0\4\54\1\0\1\36"+ + "\1\54\10\0\1\104\6\0\1\105\10\104\6\0\4\104"+ + "\2\0\1\104\24\0\1\106\46\0\1\107\15\0\1\106"+ + "\44\0\1\110\41\0\1\111\31\0\1\112\26\0\1\113"+ + "\1\114\5\0\1\115\10\113\6\0\4\113\1\0\1\114"+ + "\1\113\10\0\1\75\7\0\10\75\6\0\4\75\2\0"+ + "\1\75\11\0\1\101\5\0\1\102\23\0\1\101\12\0"+ + "\1\101\31\0\1\101\11\0\1\116\1\117\1\120\4\0"+ + "\1\121\10\116\6\0\4\116\1\0\1\117\1\116\10\0"+ + "\1\104\7\0\10\104\6\0\4\104\2\0\1\104\33\0"+ + "\1\122\37\0\1\106\41\0\1\123\63\0\1\124\24\0"+ + "\1\125\33\0\1\114\5\0\1\115\23\0\1\114\12\0"+ + "\1\114\31\0\1\114\12\0\1\117\1\120\4\0\1\121"+ + "\23\0\1\117\12\0\1\117\1\126\30\0\1\117\11\0"+ + "\1\127\6\0\1\130\10\127\6\0\4\127\2\0\1\127"+ + "\11\0\1\117\31\0\1\117\46\0\1\122\42\0\1\106"+ + "\24\0\1\106\31\0\1\131\6\0\1\132\10\131\6\0"+ + "\4\131\2\0\1\131\10\0\1\133\7\0\10\133\6\0"+ + "\4\133\2\0\1\133\10\0\1\127\7\0\10\127\6\0"+ + "\4\127\2\0\1\127\10\0\1\134\1\135\5\0\1\136"+ + "\10\134\6\0\4\134\1\0\1\135\1\134\10\0\1\131"+ + "\7\0\10\131\6\0\4\131\2\0\1\131\11\0\1\135"+ + "\5\0\1\136\23\0\1\135\12\0\1\135\31\0\1\135"+ + "\10\0"; + + private static int [] zzUnpackTrans() { + int [] result = new int[2627]; + int offset = 0; + offset = zzUnpackTrans(ZZ_TRANS_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackTrans(String packed, int offset, int [] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int count = packed.charAt(i++); + int value = packed.charAt(i++); + value--; + do result[j++] = value; while (--count > 0); + } + return j; + } + + + /* error codes */ + private static final int ZZ_UNKNOWN_ERROR = 0; + private static final int ZZ_NO_MATCH = 1; + private static final int ZZ_PUSHBACK_2BIG = 2; + + /* error messages for the codes above */ + private static final String ZZ_ERROR_MSG[] = { + "Unkown internal scanner error", + "Error: could not match input", + "Error: pushback value was too large" + }; + + /** + * ZZ_ATTRIBUTE[aState] contains the attributes of state aState + */ + private static final int [] ZZ_ATTRIBUTE = zzUnpackAttribute(); + + private static final String ZZ_ATTRIBUTE_PACKED_0 = + "\1\0\3\1\4\11\1\0\2\1\1\11\1\0\1\11"+ + "\1\0\4\11\1\1\1\11\2\1\1\11\2\1\1\0"+ + "\1\11\1\0\1\1\3\0\2\1\5\0\3\11\1\1"+ + "\1\0\1\1\1\0\1\1\2\0\1\1\1\0\2\1"+ + "\10\0\1\11\1\0\1\1\1\0\1\1\10\0\1\1"+ + "\2\0\1\1\2\0\1\11\4\0\1\1\3\0\1\11"+ + "\1\0\1\1\1\0"; + + private static int [] zzUnpackAttribute() { + int [] result = new int[94]; + int offset = 0; + offset = zzUnpackAttribute(ZZ_ATTRIBUTE_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackAttribute(String packed, int offset, int [] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int count = packed.charAt(i++); + int value = packed.charAt(i++); + do result[j++] = value; while (--count > 0); + } + return j; + } + + /** the input device */ + private java.io.Reader zzReader; + + /** the current state of the DFA */ + private int zzState; + + /** the current lexical state */ + private int zzLexicalState = YYINITIAL; + + /** this buffer contains the current text to be matched and is + the source of the yytext() string */ + private char zzBuffer[] = new char[ZZ_BUFFERSIZE]; + + /** the textposition at the last accepting state */ + private int zzMarkedPos; + + /** the textposition at the last state to be included in yytext */ + private int zzPushbackPos; + + /** the current text position in the buffer */ + private int zzCurrentPos; + + /** startRead marks the beginning of the yytext() string in the buffer */ + private int zzStartRead; + + /** endRead marks the last character in the buffer, that has been read + from input */ + private int zzEndRead; + + /** number of newlines encountered up to the start of the matched text */ + private int yyline; + + /** the number of characters up to the start of the matched text */ + private int yychar; + + /** + * the number of characters from the last newline up to the start of the + * matched text + */ + private int yycolumn; + + /** + * zzAtBOL == true <=> the scanner is currently at the beginning of a line + */ + private boolean zzAtBOL = true; + + /** zzAtEOF == true <=> the scanner is at the EOF */ + private boolean zzAtEOF; + + /* user code: */ + int getPos() { return yychar ; } + private boolean emptyString; + private ExternalSheet externalSheet; + private WorkbookMethods nameTable; + + void setExternalSheet(ExternalSheet es) + { + externalSheet = es; + } + + void setNameTable(WorkbookMethods nt) + { + nameTable = nt; + } + + + /** + * Creates a new scanner + * There is also a java.io.InputStream version of this constructor. + * + * @param in the java.io.Reader to read input from. + */ + Yylex(java.io.Reader in) { + this.zzReader = in; + } + + /** + * Creates a new scanner. + * There is also java.io.Reader version of this constructor. + * + * @param in the java.io.Inputstream to read input from. + */ + Yylex(java.io.InputStream in) { + this(new java.io.InputStreamReader(in)); + } + + /** + * Unpacks the compressed character translation table. + * + * @param packed the packed character translation table + * @return the unpacked character translation table + */ + private static char [] zzUnpackCMap(String packed) { + char [] map = new char[0x10000]; + int i = 0; /* index in packed string */ + int j = 0; /* index in unpacked array */ + while (i < 100) { + int count = packed.charAt(i++); + char value = packed.charAt(i++); + do map[j++] = value; while (--count > 0); + } + return map; + } + + + /** + * Refills the input buffer. + * + * @return false, iff there was new input. + * + * @exception java.io.IOException if any I/O-Error occurs + */ + private boolean zzRefill() throws java.io.IOException { + + /* first: make room (if you can) */ + if (zzStartRead > 0) { + System.arraycopy(zzBuffer, zzStartRead, + zzBuffer, 0, + zzEndRead-zzStartRead); + + /* translate stored positions */ + zzEndRead-= zzStartRead; + zzCurrentPos-= zzStartRead; + zzMarkedPos-= zzStartRead; + zzPushbackPos-= zzStartRead; + zzStartRead = 0; + } + + /* is the buffer big enough? */ + if (zzCurrentPos >= zzBuffer.length) { + /* if not: blow it up */ + char newBuffer[] = new char[zzCurrentPos*2]; + System.arraycopy(zzBuffer, 0, newBuffer, 0, zzBuffer.length); + zzBuffer = newBuffer; + } + + /* finally: fill the buffer with new input */ + int numRead = zzReader.read(zzBuffer, zzEndRead, + zzBuffer.length-zzEndRead); + + if (numRead < 0) { + return true; + } + else { + zzEndRead+= numRead; + return false; + } + } + + + /** + * Closes the input stream. + */ + public final void yyclose() throws java.io.IOException { + zzAtEOF = true; /* indicate end of file */ + zzEndRead = zzStartRead; /* invalidate buffer */ + + if (zzReader != null) + zzReader.close(); + } + + + /** + * Resets the scanner to read from a new input stream. + * Does not close the old reader. + * + * All internal variables are reset, the old input stream + * cannot be reused (internal buffer is discarded and lost). + * Lexical state is set to ZZ_INITIAL. + * + * @param reader the new input stream + */ + public final void yyreset(java.io.Reader reader) { + zzReader = reader; + zzAtBOL = true; + zzAtEOF = false; + zzEndRead = zzStartRead = 0; + zzCurrentPos = zzMarkedPos = zzPushbackPos = 0; + yyline = yychar = yycolumn = 0; + zzLexicalState = YYINITIAL; + } + + + /** + * Returns the current lexical state. + */ + public final int yystate() { + return zzLexicalState; + } + + + /** + * Enters a new lexical state + * + * @param newState the new lexical state + */ + public final void yybegin(int newState) { + zzLexicalState = newState; + } + + + /** + * Returns the text matched by the current regular expression. + */ + public final String yytext() { + return new String( zzBuffer, zzStartRead, zzMarkedPos-zzStartRead ); + } + + + /** + * Returns the character at position pos from the + * matched text. + * + * It is equivalent to yytext().charAt(pos), but faster + * + * @param pos the position of the character to fetch. + * A value from 0 to yylength()-1. + * + * @return the character at position pos + */ + public final char yycharat(int pos) { + return zzBuffer[zzStartRead+pos]; + } + + + /** + * Returns the length of the matched text region. + */ + public final int yylength() { + return zzMarkedPos-zzStartRead; + } + + + /** + * Reports an error that occured while scanning. + * + * In a wellformed scanner (no or only correct usage of + * yypushback(int) and a match-all fallback rule) this method + * will only be called with things that "Can't Possibly Happen". + * If this method is called, something is seriously wrong + * (e.g. a JFlex bug producing a faulty scanner etc.). + * + * Usual syntax/scanner level error handling should be done + * in error fallback rules. + * + * @param errorCode the code of the errormessage to display + */ + private void zzScanError(int errorCode) { + String message; + try { + message = ZZ_ERROR_MSG[errorCode]; + } + catch (ArrayIndexOutOfBoundsException e) { + message = ZZ_ERROR_MSG[ZZ_UNKNOWN_ERROR]; + } + + throw new Error(message); + } + + + /** + * Pushes the specified amount of characters back into the input stream. + * + * They will be read again by then next call of the scanning method + * + * @param number the number of characters to be read again. + * This number must not be greater than yylength()! + */ + public void yypushback(int number) { + if ( number > yylength() ) + zzScanError(ZZ_PUSHBACK_2BIG); + + zzMarkedPos -= number; + } + + + /** + * Resumes scanning until the next regular expression is matched, + * the end of input is encountered or an I/O-Error occurs. + * + * @return the next token + * @exception java.io.IOException if any I/O-Error occurs + */ + public ParseItem yylex() throws java.io.IOException, jxl.biff.formula.FormulaException + { + int zzInput; + int zzAction; + + // cached fields: + int zzCurrentPosL; + int zzMarkedPosL; + int zzEndReadL = zzEndRead; + char [] zzBufferL = zzBuffer; + char [] zzCMapL = ZZ_CMAP; + + int [] zzTransL = ZZ_TRANS; + int [] zzRowMapL = ZZ_ROWMAP; + int [] zzAttrL = ZZ_ATTRIBUTE; + + while (true) { + zzMarkedPosL = zzMarkedPos; + + yychar+= zzMarkedPosL-zzStartRead; + + boolean zzR = false; + for (zzCurrentPosL = zzStartRead; zzCurrentPosL < zzMarkedPosL; + zzCurrentPosL++) { + switch (zzBufferL[zzCurrentPosL]) { + case '\u000B': + case '\u000C': + case '\u0085': + case '\u2028': + case '\u2029': + yyline++; + zzR = false; + break; + case '\r': + yyline++; + zzR = true; + break; + case '\n': + if (zzR) + zzR = false; + else { + yyline++; + } + break; + default: + zzR = false; + } + } + + if (zzR) { + // peek one character ahead if it is \n (if we have counted one line too much) + boolean zzPeek; + if (zzMarkedPosL < zzEndReadL) + zzPeek = zzBufferL[zzMarkedPosL] == '\n'; + else if (zzAtEOF) + zzPeek = false; + else { + boolean eof = zzRefill(); + zzEndReadL = zzEndRead; + zzMarkedPosL = zzMarkedPos; + zzBufferL = zzBuffer; + if (eof) + zzPeek = false; + else + zzPeek = zzBufferL[zzMarkedPosL] == '\n'; + } + if (zzPeek) yyline--; + } + zzAction = -1; + + zzCurrentPosL = zzCurrentPos = zzStartRead = zzMarkedPosL; + + zzState = zzLexicalState; + + + zzForAction: { + while (true) { + + if (zzCurrentPosL < zzEndReadL) + zzInput = zzBufferL[zzCurrentPosL++]; + else if (zzAtEOF) { + zzInput = YYEOF; + break zzForAction; + } + else { + // store back cached positions + zzCurrentPos = zzCurrentPosL; + zzMarkedPos = zzMarkedPosL; + boolean eof = zzRefill(); + // get translated positions and possibly new buffer + zzCurrentPosL = zzCurrentPos; + zzMarkedPosL = zzMarkedPos; + zzBufferL = zzBuffer; + zzEndReadL = zzEndRead; + if (eof) { + zzInput = YYEOF; + break zzForAction; + } + else { + zzInput = zzBufferL[zzCurrentPosL++]; + } + } + int zzNext = zzTransL[ zzRowMapL[zzState] + zzCMapL[zzInput] ]; + if (zzNext == -1) break zzForAction; + zzState = zzNext; + + int zzAttributes = zzAttrL[zzState]; + if ( (zzAttributes & 1) == 1 ) { + zzAction = zzState; + zzMarkedPosL = zzCurrentPosL; + if ( (zzAttributes & 8) == 8 ) break zzForAction; + } + + } + } + + // store back cached position + zzMarkedPos = zzMarkedPosL; + + switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) { + case 12: + { return new Minus() ; + } + case 31: break; + case 7: + { return new CloseParentheses() ; + } + case 32: break; + case 3: + { return new IntegerValue(yytext()) ; + } + case 33: break; + case 24: + { return new DoubleValue(yytext()) ; + } + case 34: break; + case 29: + { return new ColumnRange3d(yytext(),externalSheet) ; + } + case 35: break; + case 4: + { return new RangeSeparator(); + } + case 36: break; + case 10: + { return new Divide() ; + } + case 37: break; + case 25: + { return new CellReference3d(yytext(),externalSheet) ; + } + case 38: break; + case 26: + { return new BooleanValue(yytext()); + } + case 39: break; + case 15: + { return new Equal() ; + } + case 40: break; + case 17: + { yybegin(YYINITIAL); if (emptyString) return new StringValue(""); + } + case 41: break; + case 8: + { emptyString = true; yybegin(YYSTRING); + } + case 42: break; + case 21: + { return new NotEqual() ; + } + case 43: break; + case 22: + { return new LessEqual() ; + } + case 44: break; + case 16: + { return new LessThan() ; + } + case 45: break; + case 5: + { return new ArgumentSeparator() ; + } + case 46: break; + case 30: + { return new Area3d(yytext(),externalSheet) ; + } + case 47: break; + case 14: + { return new GreaterThan() ; + } + case 48: break; + case 18: + { return new CellReference(yytext()) ; + } + case 49: break; + case 20: + { return new GreaterEqual() ; + } + case 50: break; + case 27: + { return new Area(yytext()) ; + } + case 51: break; + case 23: + { return new ColumnRange(yytext()); + } + case 52: break; + case 1: + { emptyString = false; return new StringValue(yytext()) ; + } + case 53: break; + case 2: + { return new NameRange(yytext(), nameTable); + } + case 54: break; + case 19: + { return new StringFunction(yytext()) ; + } + case 55: break; + case 11: + { return new Plus() ; + } + case 56: break; + case 28: + { return new ErrorConstant(yytext()); + } + case 57: break; + case 9: + { + } + case 58: break; + case 13: + { return new Multiply() ; + } + case 59: break; + case 6: + { return new OpenParentheses() ; + } + case 60: break; + default: + if (zzInput == YYEOF && zzStartRead == zzCurrentPos) { + zzAtEOF = true; + return null; + } + else { + zzScanError(ZZ_NO_MATCH); + } + } + } + } + + +} Index: 3rdParty_sources/jexcelapi/jxl/demo/BiffDump.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/demo/BiffDump.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/demo/BiffDump.java 17 Aug 2012 14:51:17 -0000 1.1 @@ -0,0 +1,367 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.demo; + + +import java.io.BufferedWriter; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; + +import java.util.HashMap; + +import jxl.WorkbookSettings; +import jxl.biff.Type; +import jxl.read.biff.BiffException; +import jxl.read.biff.BiffRecordReader; +import jxl.read.biff.File; +import jxl.read.biff.Record; + +/** + * Generates a biff dump of the specified excel file + */ +class BiffDump +{ + private BufferedWriter writer; + private BiffRecordReader reader; + + private HashMap recordNames; + + private int xfIndex; + private int fontIndex; + private int bofs; + + private static final int bytesPerLine = 16; + + /** + * Constructor + * + * @param file the file + * @param os the output stream + * @exception IOException + * @exception BiffException + */ + public BiffDump(java.io.File file, OutputStream os) + throws IOException, BiffException + { + writer = new BufferedWriter(new OutputStreamWriter(os)); + FileInputStream fis = new FileInputStream(file); + File f = new File(fis, new WorkbookSettings()); + reader = new BiffRecordReader(f); + + buildNameHash(); + dump(); + + writer.flush(); + writer.close(); + fis.close(); + } + + /** + * Builds the hashmap of record names + */ + private void buildNameHash() + { + recordNames = new HashMap(50); + + recordNames.put(Type.BOF, "BOF"); + recordNames.put(Type.EOF, "EOF"); + recordNames.put(Type.FONT, "FONT"); + recordNames.put(Type.SST, "SST"); + recordNames.put(Type.LABELSST, "LABELSST"); + recordNames.put(Type.WRITEACCESS, "WRITEACCESS"); + recordNames.put(Type.FORMULA, "FORMULA"); + recordNames.put(Type.FORMULA2, "FORMULA"); + recordNames.put(Type.XF, "XF"); + recordNames.put(Type.MULRK, "MULRK"); + recordNames.put(Type.NUMBER, "NUMBER"); + recordNames.put(Type.BOUNDSHEET, "BOUNDSHEET"); + recordNames.put(Type.CONTINUE, "CONTINUE"); + recordNames.put(Type.FORMAT, "FORMAT"); + recordNames.put(Type.EXTERNSHEET, "EXTERNSHEET"); + recordNames.put(Type.INDEX, "INDEX"); + recordNames.put(Type.DIMENSION, "DIMENSION"); + recordNames.put(Type.ROW, "ROW"); + recordNames.put(Type.DBCELL, "DBCELL"); + recordNames.put(Type.BLANK, "BLANK"); + recordNames.put(Type.MULBLANK, "MULBLANK"); + recordNames.put(Type.RK, "RK"); + recordNames.put(Type.RK2, "RK"); + recordNames.put(Type.COLINFO, "COLINFO"); + recordNames.put(Type.LABEL, "LABEL"); + recordNames.put(Type.SHAREDFORMULA, "SHAREDFORMULA"); + recordNames.put(Type.CODEPAGE, "CODEPAGE"); + recordNames.put(Type.WINDOW1, "WINDOW1"); + recordNames.put(Type.WINDOW2, "WINDOW2"); + recordNames.put(Type.MERGEDCELLS, "MERGEDCELLS"); + recordNames.put(Type.HLINK, "HLINK"); + recordNames.put(Type.HEADER, "HEADER"); + recordNames.put(Type.FOOTER, "FOOTER"); + recordNames.put(Type.INTERFACEHDR, "INTERFACEHDR"); + recordNames.put(Type.MMS, "MMS"); + recordNames.put(Type.INTERFACEEND, "INTERFACEEND"); + recordNames.put(Type.DSF, "DSF"); + recordNames.put(Type.FNGROUPCOUNT, "FNGROUPCOUNT"); + recordNames.put(Type.COUNTRY, "COUNTRY"); + recordNames.put(Type.TABID, "TABID"); + recordNames.put(Type.PROTECT, "PROTECT"); + recordNames.put(Type.SCENPROTECT, "SCENPROTECT"); + recordNames.put(Type.OBJPROTECT, "OBJPROTECT"); + recordNames.put(Type.WINDOWPROTECT, "WINDOWPROTECT"); + recordNames.put(Type.PASSWORD, "PASSWORD"); + recordNames.put(Type.PROT4REV, "PROT4REV"); + recordNames.put(Type.PROT4REVPASS, "PROT4REVPASS"); + recordNames.put(Type.BACKUP, "BACKUP"); + recordNames.put(Type.HIDEOBJ, "HIDEOBJ"); + recordNames.put(Type.NINETEENFOUR, "1904"); + recordNames.put(Type.PRECISION, "PRECISION"); + recordNames.put(Type.BOOKBOOL, "BOOKBOOL"); + recordNames.put(Type.STYLE, "STYLE"); + recordNames.put(Type.EXTSST, "EXTSST"); + recordNames.put(Type.REFRESHALL, "REFRESHALL"); + recordNames.put(Type.CALCMODE, "CALCMODE"); + recordNames.put(Type.CALCCOUNT, "CALCCOUNT"); + recordNames.put(Type.NAME, "NAME"); + recordNames.put(Type.MSODRAWINGGROUP, "MSODRAWINGGROUP"); + recordNames.put(Type.MSODRAWING, "MSODRAWING"); + recordNames.put(Type.OBJ, "OBJ"); + recordNames.put(Type.USESELFS, "USESELFS"); + recordNames.put(Type.SUPBOOK, "SUPBOOK"); + recordNames.put(Type.LEFTMARGIN, "LEFTMARGIN"); + recordNames.put(Type.RIGHTMARGIN, "RIGHTMARGIN"); + recordNames.put(Type.TOPMARGIN, "TOPMARGIN"); + recordNames.put(Type.BOTTOMMARGIN, "BOTTOMMARGIN"); + recordNames.put(Type.HCENTER, "HCENTER"); + recordNames.put(Type.VCENTER, "VCENTER"); + recordNames.put(Type.ITERATION, "ITERATION"); + recordNames.put(Type.DELTA, "DELTA"); + recordNames.put(Type.SAVERECALC, "SAVERECALC"); + recordNames.put(Type.PRINTHEADERS, "PRINTHEADERS"); + recordNames.put(Type.PRINTGRIDLINES, "PRINTGRIDLINES"); + recordNames.put(Type.SETUP, "SETUP"); + recordNames.put(Type.SELECTION, "SELECTION"); + recordNames.put(Type.STRING, "STRING"); + recordNames.put(Type.FONTX, "FONTX"); + recordNames.put(Type.IFMT, "IFMT"); + recordNames.put(Type.WSBOOL, "WSBOOL"); + recordNames.put(Type.GRIDSET, "GRIDSET"); + recordNames.put(Type.REFMODE, "REFMODE"); + recordNames.put(Type.GUTS, "GUTS"); + recordNames.put(Type.EXTERNNAME, "EXTERNNAME"); + recordNames.put(Type.FBI, "FBI"); + recordNames.put(Type.CRN, "CRN"); + recordNames.put(Type.HORIZONTALPAGEBREAKS, "HORIZONTALPAGEBREAKS"); + recordNames.put(Type.VERTICALPAGEBREAKS, "VERTICALPAGEBREAKS"); + recordNames.put(Type.DEFAULTROWHEIGHT, "DEFAULTROWHEIGHT"); + recordNames.put(Type.TEMPLATE, "TEMPLATE"); + recordNames.put(Type.PANE, "PANE"); + recordNames.put(Type.SCL, "SCL"); + recordNames.put(Type.PALETTE, "PALETTE"); + recordNames.put(Type.PLS, "PLS"); + recordNames.put(Type.OBJPROJ, "OBJPROJ"); + recordNames.put(Type.DEFCOLWIDTH, "DEFCOLWIDTH"); + recordNames.put(Type.ARRAY, "ARRAY"); + recordNames.put(Type.WEIRD1, "WEIRD1"); + recordNames.put(Type.BOOLERR, "BOOLERR"); + recordNames.put(Type.SORT, "SORT"); + recordNames.put(Type.BUTTONPROPERTYSET, "BUTTONPROPERTYSET"); + recordNames.put(Type.NOTE, "NOTE"); + recordNames.put(Type.TXO, "TXO"); + recordNames.put(Type.DV, "DV"); + recordNames.put(Type.DVAL, "DVAL"); + recordNames.put(Type.SERIES, "SERIES"); + recordNames.put(Type.SERIESLIST, "SERIESLIST"); + recordNames.put(Type.SBASEREF, "SBASEREF"); + recordNames.put(Type.CONDFMT, "CONDFMT"); + recordNames.put(Type.CF, "CF"); + recordNames.put(Type.FILTERMODE, "FILTERMODE"); + recordNames.put(Type.AUTOFILTER, "AUTOFILTER"); + recordNames.put(Type.AUTOFILTERINFO, "AUTOFILTERINFO"); + + recordNames.put(Type.UNKNOWN, "???"); + } + /** + * Dumps out the contents of the excel file + */ + private void dump() throws IOException + { + Record r = null; + boolean cont = true; + while (reader.hasNext() && cont) + { + r = reader.next(); + cont = writeRecord(r); + } + } + + /** + * Writes out the biff record + * @param r + * @exception IOException if an error occurs + */ + private boolean writeRecord(Record r) + throws IOException + { + boolean cont = true; + int pos = reader.getPos(); + int code = r.getCode(); + + if (bofs == 0) + { + cont = (r.getType() == Type.BOF); + } + + if (!cont) + { + return cont; + } + + if (r.getType() == Type.BOF) + { + bofs++; + } + + if (r.getType() == Type.EOF) + { + bofs--; + } + + StringBuffer buf = new StringBuffer(); + + // Write out the record header + writeSixDigitValue(pos, buf); + buf.append(" ["); + buf.append(recordNames.get(r.getType())); + buf.append("]"); + buf.append(" (0x"); + buf.append(Integer.toHexString(code)); + buf.append(")"); + + if (code == Type.XF.value) + { + buf.append(" (0x"); + buf.append(Integer.toHexString(xfIndex)); + buf.append(")"); + xfIndex++; + } + + if (code == Type.FONT.value) + { + if (fontIndex == 4) + { + fontIndex++; + } + + buf.append(" (0x"); + buf.append(Integer.toHexString(fontIndex)); + buf.append(")"); + fontIndex++; + } + + writer.write(buf.toString()); + writer.newLine(); + + byte[] standardData = new byte[4]; + standardData[0] = (byte) (code & 0xff); + standardData[1] = (byte) ((code & 0xff00) >> 8); + standardData[2] = (byte) (r.getLength() & 0xff); + standardData[3] = (byte) ((r.getLength() & 0xff00) >> 8); + byte[] recordData = r.getData(); + byte[] data = new byte[standardData.length + recordData.length]; + System.arraycopy(standardData, 0, data, 0, standardData.length); + System.arraycopy(recordData, 0, data, + standardData.length, recordData.length); + + int byteCount = 0; + int lineBytes = 0; + + while (byteCount < data.length) + { + buf = new StringBuffer(); + writeSixDigitValue(pos+byteCount, buf); + buf.append(" "); + + lineBytes = Math.min(bytesPerLine, data.length - byteCount); + + for (int i = 0; i < lineBytes ; i++) + { + writeByte(data[i+byteCount], buf); + buf.append(' '); + } + + // Perform any padding + if (lineBytes < bytesPerLine) + { + for(int i = 0; i < bytesPerLine - lineBytes; i++) + { + buf.append(" "); + } + } + + buf.append(" "); + + for (int i = 0 ; i < lineBytes; i++) + { + char c = (char) data[i+byteCount]; + if (c < ' ' || c > 'z') + { + c = '.'; + } + buf.append(c); + } + + byteCount+= lineBytes; + + writer.write(buf.toString()); + writer.newLine(); + } + + return cont; + } + + /** + * Writes the string passed in as a minimum of four digits + */ + private void writeSixDigitValue(int pos, StringBuffer buf) + { + String val = Integer.toHexString(pos); + + for (int i = 6; i > val.length() ; i--) + { + buf.append('0'); + } + buf.append(val); + } + + /** + * Writes the string passed in as a minimum of four digits + */ + private void writeByte(byte val, StringBuffer buf) + { + String sv = Integer.toHexString((val & 0xff)); + + if (sv.length() == 1) + { + buf.append('0'); + } + buf.append(sv); + } +} Index: 3rdParty_sources/jexcelapi/jxl/demo/CSV.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/demo/CSV.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/demo/CSV.java 17 Aug 2012 14:51:17 -0000 1.1 @@ -0,0 +1,112 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.demo; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; + +import jxl.Cell; +import jxl.Sheet; +import jxl.Workbook; + +/** + * Simple demo class which uses the api to present the contents + * of an excel 97 spreadsheet as comma separated values, using a workbook + * and output stream of your choice + */ +public class CSV +{ + /** + * Constructor + * + * @param w The workbook to interrogate + * @param out The output stream to which the CSV values are written + * @param encoding The encoding used by the output stream. Null or + * unrecognized values cause the encoding to default to UTF8 + * @param hide Suppresses hidden cells + * @exception java.io.IOException + */ + public CSV(Workbook w, OutputStream out, String encoding, boolean hide) + throws IOException + { + if (encoding == null || !encoding.equals("UnicodeBig")) + { + encoding = "UTF8"; + } + + try + { + OutputStreamWriter osw = new OutputStreamWriter(out, encoding); + BufferedWriter bw = new BufferedWriter(osw); + + for (int sheet = 0; sheet < w.getNumberOfSheets(); sheet++) + { + Sheet s = w.getSheet(sheet); + + if (!(hide && s.getSettings().isHidden())) + { + bw.write("*** " + s.getName() + " ****"); + bw.newLine(); + + Cell[] row = null; + + for (int i = 0 ; i < s.getRows() ; i++) + { + row = s.getRow(i); + + if (row.length > 0) + { + if (!(hide && row[0].isHidden())) + { + bw.write(row[0].getContents()); + // Java 1.4 code to handle embedded commas + // bw.write("\"" + row[0].getContents().replaceAll("\"","\"\"") + "\""); + } + + for (int j = 1; j < row.length; j++) + { + bw.write(','); + if (!(hide && row[j].isHidden())) + { + bw.write(row[j].getContents()); + // Java 1.4 code to handle embedded quotes + // bw.write("\"" + row[j].getContents().replaceAll("\"","\"\"") + "\""); + } + } + } + bw.newLine(); + } + } + } + bw.flush(); + bw.close(); + } + catch (UnsupportedEncodingException e) + { + System.err.println(e.toString()); + } + } +} + + + Index: 3rdParty_sources/jexcelapi/jxl/demo/Demo.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/demo/Demo.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/demo/Demo.java 17 Aug 2012 14:51:17 -0000 1.1 @@ -0,0 +1,366 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.demo; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.OutputStream; + +import common.Logger; + +import jxl.Cell; +import jxl.Range; +import jxl.Workbook; + +/** + * The main demo class which interprets the command line switches in order + * to determine how to call the demo programs + * The demo program uses stdout as its default output stream + */ +public class Demo +{ + private static final int CSVFormat = 13; + private static final int XMLFormat = 14; + + /** + * The logger + */ + private static Logger logger = Logger.getLogger(Demo.class); + + /** + * Displays the acceptable command line arguments + */ + private static void displayHelp() + { + System.err.println + ("Command format: Demo [-unicode] [-csv] [-hide] excelfile"); + System.err.println(" Demo -xml [-format] excelfile"); + System.err.println(" Demo -readwrite|-rw excelfile output"); + System.err.println(" Demo -biffdump | -bd | -wa | -write | -formulas | -features | -escher | -escherdg excelfile"); + System.err.println(" Demo -ps excelfile [property] [output]"); + System.err.println(" Demo -version | -logtest | -h | -help"); + + } + + /** + * The main method. Gets the worksheet and then uses the API + * within a simple loop to print out the spreadsheet contents as + * comma separated values + * + * @param args the command line arguments + */ + public static void main(String args[]) + { + if (args.length == 0) + { + displayHelp(); + System.exit(1); + } + + if (args[0].equals("-help") || args[0].equals("-h")) + { + displayHelp(); + System.exit(1); + } + + if (args[0].equals("-version")) + { + System.out.println("v"+Workbook.getVersion()); + System.exit(0); + } + + if (args[0].equals("-logtest")) + { + logger.debug("A sample \"debug\" message"); + logger.info("A sample \"info\" message"); + logger.warn("A sample \"warning\" message"); + logger.error("A sample \"error\" message"); + logger.fatal("A sample \"fatal\" message"); + System.exit(0); + } + + boolean write = false; + boolean readwrite = false; + boolean formulas = false; + boolean biffdump = false; + boolean jxlversion = false; + boolean propertysets = false; + boolean features = false; + boolean escher = false; + boolean escherdg = false; + String file = args[0]; + String outputFile = null; + String propertySet = null; + + if (args[0].equals("-write")) + { + write = true; + file = args[1]; + } + else if (args[0].equals("-formulas")) + { + formulas = true; + file = args[1]; + } + else if (args[0].equals("-features")) + { + features = true; + file = args[1]; + } + else if (args[0].equals("-escher")) + { + escher = true; + file = args[1]; + } + else if (args[0].equals("-escherdg")) + { + escherdg = true; + file = args[1]; + } + else if (args[0].equals("-biffdump") || args[0].equals("-bd")) + { + biffdump = true; + file = args[1]; + } + else if (args[0].equals("-wa")) + { + jxlversion = true; + file = args[1]; + } + else if (args[0].equals("-ps")) + { + propertysets = true; + file = args[1]; + + if (args.length > 2) + { + propertySet = args[2]; + } + + if (args.length == 4) + { + outputFile = args[3]; + } + } + else if (args[0].equals("-readwrite") || args[0].equals("-rw")) + { + readwrite = true; + file = args[1]; + outputFile = args[2]; + } + else + { + file = args[args.length - 1]; + } + + String encoding = "UTF8"; + int format = CSVFormat; + boolean formatInfo = false; + boolean hideCells = false; + + if (write == false && + readwrite == false && + formulas == false && + biffdump == false && + jxlversion == false && + propertysets == false && + features == false && + escher == false && + escherdg == false) + { + for (int i = 0; i < args.length - 1; i++) + { + if (args[i].equals("-unicode")) + { + encoding="UnicodeBig"; + } + else if (args[i].equals("-xml")) + { + format = XMLFormat; + } + else if (args[i].equals("-csv")) + { + format = CSVFormat; + } + else if (args[i].equals("-format")) + { + formatInfo = true; + } + else if (args[i].equals("-hide")) + { + hideCells = true; + } + else + { + System.err.println + ("Command format: CSV [-unicode] [-xml|-csv] excelfile"); + System.exit(1); + } + } + } + + try + { + if (write) + { + Write w = new Write(file); + w.write(); + } + else if (readwrite) + { + ReadWrite rw = new ReadWrite(file, outputFile); + rw.readWrite(); + } + else if (formulas) + { + Workbook w = Workbook.getWorkbook(new File(file)); + Formulas f = new Formulas(w, System.out, encoding); + w.close(); + } + else if (features) + { + Workbook w = Workbook.getWorkbook(new File(file)); + Features f = new Features(w, System.out, encoding); + w.close(); + } + else if (escher) + { + Workbook w = Workbook.getWorkbook(new File(file)); + Escher f = new Escher(w, System.out, encoding); + w.close(); + } + else if (escherdg) + { + Workbook w = Workbook.getWorkbook(new File(file)); + EscherDrawingGroup f = new EscherDrawingGroup(w, System.out, encoding); + w.close(); + } + else if (biffdump) + { + BiffDump bd = new BiffDump(new File(file), System.out); + } + else if (jxlversion) + { + WriteAccess bd = new WriteAccess(new File(file)); + } + else if (propertysets) + { + OutputStream os = System.out; + if (outputFile != null) + { + os = new FileOutputStream(outputFile); + } + PropertySetsReader psr = new PropertySetsReader(new File(file), + propertySet, + os); + } + else + { + Workbook w = Workbook.getWorkbook(new File(file)); + + // findTest(w); + + if (format == CSVFormat) + { + CSV csv = new CSV(w, System.out, encoding, hideCells); + } + else if (format == XMLFormat) + { + XML xml = new XML(w, System.out, encoding, formatInfo); + } + + w.close(); + } + } + catch (Throwable t) + { + System.out.println(t.toString()); + t.printStackTrace(); + } + } + + /** + * A private method to test the various find functions + */ + private static void findTest(Workbook w) + { + logger.info("Find test"); + + Cell c = w.findCellByName("named1"); + if (c != null) + { + logger.info("named1 contents: " + c.getContents()); + } + + c = w.findCellByName("named2"); + if (c != null) + { + logger.info("named2 contents: " + c.getContents()); + } + + c = w.findCellByName("namedrange"); + if (c != null) + { + logger.info("named2 contents: " + c.getContents()); + } + + Range[] range = w.findByName("namedrange"); + if (range != null) + { + c = range[0].getTopLeft(); + logger.info("namedrange top left contents: " + c.getContents()); + + c = range[0].getBottomRight(); + logger.info("namedrange bottom right contents: " + c.getContents()); + } + + range = w.findByName("nonadjacentrange"); + if (range != null) + { + for (int i = 0; i < range.length; i++) + { + c = range[i].getTopLeft(); + logger.info("nonadjacent top left contents: " + c.getContents()); + + c = range[i].getBottomRight(); + logger.info("nonadjacent bottom right contents: " + c.getContents()); + } + } + + range = w.findByName("horizontalnonadjacentrange"); + if (range != null) + { + for (int i = 0; i < range.length; i++) + { + c = range[i].getTopLeft(); + logger.info("horizontalnonadjacent top left contents: " + + c.getContents()); + + c = range[i].getBottomRight(); + logger.info("horizontalnonadjacent bottom right contents: " + + c.getContents()); + } + } + + } +} + + + Index: 3rdParty_sources/jexcelapi/jxl/demo/Escher.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/demo/Escher.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/demo/Escher.java 17 Aug 2012 14:51:17 -0000 1.1 @@ -0,0 +1,89 @@ +/********************************************************************* +* +* Copyright (C) 2005 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.demo; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; + +import jxl.Workbook; +import jxl.biff.drawing.DrawingData; +import jxl.biff.drawing.EscherDisplay; +import jxl.read.biff.SheetImpl; + +/** + * Displays the escher data + */ +public class Escher +{ + /** + * Constructor + * + * @param w The workbook to interrogate + * @param out The output stream to which the CSV values are written + * @param encoding The encoding used by the output stream. Null or + * unrecognized values cause the encoding to default to UTF8 + * @exception java.io.IOException + */ + public Escher (Workbook w, OutputStream out, String encoding) + throws IOException + { + if (encoding == null || !encoding.equals("UnicodeBig")) + { + encoding = "UTF8"; + } + + try + { + OutputStreamWriter osw = new OutputStreamWriter(out, encoding); + BufferedWriter bw = new BufferedWriter(osw); + + for (int i = 0; i < w.getNumberOfSheets(); i++) + { + SheetImpl s = (SheetImpl) w.getSheet(i); + bw.write(s.getName()); + bw.newLine(); + bw.newLine(); + + DrawingData dd = s.getDrawingData(); + + if (dd != null) + { + EscherDisplay ed = new EscherDisplay(dd, bw); + ed.display(); + } + + bw.newLine(); + bw.newLine(); + bw.flush(); + } + bw.flush(); + bw.close(); + } + catch (UnsupportedEncodingException e) + { + System.err.println(e.toString()); + } + } + +} + Index: 3rdParty_sources/jexcelapi/jxl/demo/EscherDrawingGroup.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/demo/EscherDrawingGroup.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/demo/EscherDrawingGroup.java 17 Aug 2012 14:51:17 -0000 1.1 @@ -0,0 +1,82 @@ +/********************************************************************* +* +* Copyright (C) 2005 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.demo; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; + +import jxl.Workbook; +import jxl.biff.drawing.DrawingGroup; +import jxl.biff.drawing.EscherDisplay; +import jxl.read.biff.WorkbookParser; + +/** + * Displays the escher data + */ +public class EscherDrawingGroup +{ + /** + * Constructor + * + * @param w The workbook to interrogate + * @param out The output stream to which the CSV values are written + * @param encoding The encoding used by the output stream. Null or + * unrecognized values cause the encoding to default to UTF8 + * @exception java.io.IOException + */ + public EscherDrawingGroup (Workbook w, OutputStream out, String encoding) + throws IOException + { + if (encoding == null || !encoding.equals("UnicodeBig")) + { + encoding = "UTF8"; + } + + try + { + OutputStreamWriter osw = new OutputStreamWriter(out, encoding); + BufferedWriter bw = new BufferedWriter(osw); + + WorkbookParser wp = (WorkbookParser) w; + + DrawingGroup dg = wp.getDrawingGroup(); + + if (dg != null) + { + EscherDisplay ed = new EscherDisplay(dg, bw); + ed.display(); + } + + bw.newLine(); + bw.newLine(); + bw.flush(); + bw.close(); + } + catch (UnsupportedEncodingException e) + { + System.err.println(e.toString()); + } + } + +} + Index: 3rdParty_sources/jexcelapi/jxl/demo/Features.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/demo/Features.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/demo/Features.java 17 Aug 2012 14:51:17 -0000 1.1 @@ -0,0 +1,108 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.demo; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; + +import java.util.ArrayList; + +import jxl.Cell; +import jxl.CellFeatures; +import jxl.CellReferenceHelper; +import jxl.Sheet; +import jxl.Workbook; + +/** + * Goes through each cell in the workbook, and if the cell has any features + * associated with, it prints out the cell contents and the features + */ +public class Features +{ + /** + * Constructor + * + * @param w The workbook to interrogate + * @param out The output stream to which the CSV values are written + * @param encoding The encoding used by the output stream. Null or + * unrecognized values cause the encoding to default to UTF8 + * @exception java.io.IOException + */ + public Features(Workbook w, OutputStream out, String encoding) + throws IOException + { + if (encoding == null || !encoding.equals("UnicodeBig")) + { + encoding = "UTF8"; + } + + try + { + OutputStreamWriter osw = new OutputStreamWriter(out, encoding); + BufferedWriter bw = new BufferedWriter(osw); + + for (int sheet = 0; sheet < w.getNumberOfSheets(); sheet++) + { + Sheet s = w.getSheet(sheet); + + bw.write(s.getName()); + bw.newLine(); + + Cell[] row = null; + Cell c = null; + + for (int i = 0 ; i < s.getRows() ; i++) + { + row = s.getRow(i); + + for (int j = 0; j < row.length; j++) + { + c = row[j]; + if (c.getCellFeatures() != null) + { + CellFeatures features = c.getCellFeatures(); + StringBuffer sb = new StringBuffer(); + CellReferenceHelper.getCellReference + (c.getColumn(), c.getRow(), sb); + + bw.write("Cell " + sb.toString() + + " contents: " + c.getContents()); + bw.flush(); + bw.write(" comment: " + features.getComment()); + bw.flush(); + bw.newLine(); + } + } + } + } + bw.flush(); + bw.close(); + } + catch (UnsupportedEncodingException e) + { + System.err.println(e.toString()); + } + } + +} + Index: 3rdParty_sources/jexcelapi/jxl/demo/Formulas.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/demo/Formulas.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/demo/Formulas.java 17 Aug 2012 14:51:17 -0000 1.1 @@ -0,0 +1,139 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.demo; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; + +import java.util.ArrayList; +import java.util.Iterator; + +import jxl.Cell; +import jxl.CellType; +import jxl.FormulaCell; +import jxl.Sheet; +import jxl.Workbook; +import jxl.biff.CellReferenceHelper; +import jxl.biff.formula.FormulaException; + +/** + * Goes through each cell in the workbook, and if the contents of that + * cell is a formula, it prints out the last calculated value and + * the formula string + */ +public class Formulas +{ + /** + * Constructor + * + * @param w The workbook to interrogate + * @param out The output stream to which the CSV values are written + * @param encoding The encoding used by the output stream. Null or + * unrecognized values cause the encoding to default to UTF8 + * @exception java.io.IOException + */ + public Formulas(Workbook w, OutputStream out, String encoding) + throws IOException + { + if (encoding == null || !encoding.equals("UnicodeBig")) + { + encoding = "UTF8"; + } + + try + { + OutputStreamWriter osw = new OutputStreamWriter(out, encoding); + BufferedWriter bw = new BufferedWriter(osw); + + ArrayList parseErrors = new ArrayList(); + + for (int sheet = 0; sheet < w.getNumberOfSheets(); sheet++) + { + Sheet s = w.getSheet(sheet); + + bw.write(s.getName()); + bw.newLine(); + + Cell[] row = null; + Cell c = null; + + for (int i = 0 ; i < s.getRows() ; i++) + { + row = s.getRow(i); + + for (int j = 0; j < row.length; j++) + { + c = row[j]; + if (c.getType() == CellType.NUMBER_FORMULA || + c.getType() == CellType.STRING_FORMULA || + c.getType() == CellType.BOOLEAN_FORMULA || + c.getType() == CellType.DATE_FORMULA || + c.getType() == CellType.FORMULA_ERROR) + { + FormulaCell nfc = (FormulaCell) c; + StringBuffer sb = new StringBuffer(); + CellReferenceHelper.getCellReference + (c.getColumn(), c.getRow(), sb); + + try + { + bw.write("Formula in " + sb.toString() + + " value: " + c.getContents()); + bw.flush(); + bw.write(" formula: " + nfc.getFormula()); + bw.flush(); + bw.newLine(); + } + catch (FormulaException e) + { + bw.newLine(); + parseErrors.add(s.getName() + '!' + + sb.toString() + ": " + e.getMessage()); + } + } + } + } + } + bw.flush(); + bw.close(); + + if (parseErrors.size() > 0) + { + System.err.println(); + System.err.println("There were " + parseErrors.size() + " errors"); + + Iterator i = parseErrors.iterator(); + while (i.hasNext()) + { + System.err.println(i.next()); + } + } + } + catch (UnsupportedEncodingException e) + { + System.err.println(e.toString()); + } + } + +} + Index: 3rdParty_sources/jexcelapi/jxl/demo/PropertySetsReader.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/demo/PropertySetsReader.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/demo/PropertySetsReader.java 17 Aug 2012 14:51:17 -0000 1.1 @@ -0,0 +1,149 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.demo; + +import java.io.BufferedWriter; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; + +import jxl.WorkbookSettings; +import jxl.biff.BaseCompoundFile; +import jxl.read.biff.BiffException; +import jxl.read.biff.CompoundFile; + + +/** + * Generates a biff dump of the specified excel file + */ +class PropertySetsReader +{ + private BufferedWriter writer; + private CompoundFile compoundFile; + + /** + * Constructor + * + * @param file the file + * @param propertySet the property set to read + * @param os the output stream + * @exception IOException + * @exception BiffException + */ + public PropertySetsReader(java.io.File file, String propertySet, + OutputStream os) + throws IOException, BiffException + { + writer = new BufferedWriter(new OutputStreamWriter(os)); + FileInputStream fis = new FileInputStream(file); + + int initialFileSize = 1024*1024; // 1mb + int arrayGrowSize = 1024*1024;// 1mb + + byte[] d = new byte[initialFileSize]; + int bytesRead = fis.read(d); + int pos = bytesRead; + + while (bytesRead != -1) + { + if (pos >= d.length) + { + // Grow the array + byte newArray[] = new byte[d.length + arrayGrowSize]; + System.arraycopy(d, 0, newArray, 0, d.length); + d = newArray; + } + bytesRead = fis.read(d, pos, d.length - pos); + pos += bytesRead; + } + + bytesRead = pos + 1; + + compoundFile = new CompoundFile(d, new WorkbookSettings()); + fis.close(); + + if (propertySet == null) + { + displaySets(); + } + else + { + displayPropertySet(propertySet, os); + } + } + + /** + * Displays the properties to the output stream + */ + void displaySets() throws IOException + { + int numSets = compoundFile.getNumberOfPropertySets(); + + for (int i = 0; i < numSets ; i++) + { + BaseCompoundFile.PropertyStorage ps = compoundFile.getPropertySet(i); + writer.write(Integer.toString(i)); + writer.write(") "); + writer.write(ps.name); + writer.write("(type "); + writer.write(Integer.toString(ps.type)); + writer.write(" size "); + writer.write(Integer.toString(ps.size)); + writer.write(" prev " ); + writer.write(Integer.toString(ps.previous)); + writer.write(" next " ); + writer.write(Integer.toString(ps.next)); + writer.write(" child " ); + writer.write(Integer.toString(ps.child)); + writer.write(" start block " ); + writer.write(Integer.toString(ps.startBlock)); + writer.write(")"); + writer.newLine(); + } + + writer.flush(); + writer.close(); + } + + /** + * Write the property stream to the output stream + */ + void displayPropertySet(String ps, OutputStream os) + throws IOException,BiffException + { + if (ps.equalsIgnoreCase("SummaryInformation")) + { + ps = BaseCompoundFile.SUMMARY_INFORMATION_NAME; + } + else if (ps.equalsIgnoreCase("DocumentSummaryInformation")) + { + ps = BaseCompoundFile.DOCUMENT_SUMMARY_INFORMATION_NAME; + } + else if (ps.equalsIgnoreCase("CompObj")) + { + ps = BaseCompoundFile.COMP_OBJ_NAME; + } + + byte[] stream = compoundFile.getStream(ps); + os.write(stream); + } + +} Index: 3rdParty_sources/jexcelapi/jxl/demo/ReadWrite.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/demo/ReadWrite.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/demo/ReadWrite.java 17 Aug 2012 14:51:17 -0000 1.1 @@ -0,0 +1,438 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.demo; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; + +import common.Logger; + +import jxl.CellType; +import jxl.Sheet; +import jxl.Workbook; +import jxl.format.CellFormat; +import jxl.format.Colour; +import jxl.format.UnderlineStyle; +import jxl.read.biff.BiffException; +import jxl.write.Blank; +import jxl.write.DateFormat; +import jxl.write.DateFormats; +import jxl.write.DateTime; +import jxl.write.Formula; +import jxl.write.Label; +import jxl.write.Number; +import jxl.write.NumberFormat; +import jxl.write.WritableCell; +import jxl.write.WritableCellFeatures; +import jxl.write.WritableCellFormat; +import jxl.write.WritableFont; +import jxl.write.WritableHyperlink; +import jxl.write.WritableImage; +import jxl.write.WritableSheet; +import jxl.write.WritableWorkbook; +import jxl.write.WriteException; + +/** + * Demo class which uses the api to read in a spreadsheet and generate a clone + * of that spreadsheet which contains the same data. If the spreadsheet read + * in is the spreadsheet called jxlrwtest.xls (provided with the distribution) + * then this class will modify certain fields in the copy of that spreadsheet. + * This is illustrating that it is possible to read in a spreadsheet, modify + * a few values, and write it under a new name. + */ +public class ReadWrite +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(ReadWrite.class); + + /** + * The spreadsheet to read in + */ + private File inputWorkbook; + /** + * The spreadsheet to output + */ + private File outputWorkbook; + + /** + * Constructor + * + * @param output + * @param input + */ + public ReadWrite(String input, String output) + { + inputWorkbook = new File(input); + outputWorkbook = new File(output); + logger.setSuppressWarnings(Boolean.getBoolean("jxl.nowarnings")); + logger.info("Input file: " + input); + logger.info("Output file: " + output); + } + + /** + * Reads in the inputFile and creates a writable copy of it called outputFile + * + * @exception IOException + * @exception BiffException + */ + public void readWrite() throws IOException, BiffException, WriteException + { + logger.info("Reading..."); + Workbook w1 = Workbook.getWorkbook(inputWorkbook); + + logger.info("Copying..."); + WritableWorkbook w2 = Workbook.createWorkbook(outputWorkbook, w1); + + if (inputWorkbook.getName().equals("jxlrwtest.xls")) + { + modify(w2); + } + + w2.write(); + w2.close(); + logger.info("Done"); + } + + /** + * If the inputFile was the test spreadsheet, then it modifies certain fields + * of the writable copy + * + * @param w + */ + private void modify(WritableWorkbook w) throws WriteException + { + logger.info("Modifying..."); + + WritableSheet sheet = w.getSheet("modified"); + + WritableCell cell = null; + CellFormat cf = null; + Label l = null; + WritableCellFeatures wcf = null; + + // Change the format of cell B4 to be emboldened + cell = sheet.getWritableCell(1,3); + WritableFont bold = new WritableFont(WritableFont.ARIAL, + WritableFont.DEFAULT_POINT_SIZE, + WritableFont.BOLD); + cf = new WritableCellFormat(bold); + cell.setCellFormat(cf); + + // Change the format of cell B5 to be underlined + cell = sheet.getWritableCell(1,4); + WritableFont underline = new WritableFont(WritableFont.ARIAL, + WritableFont.DEFAULT_POINT_SIZE, + WritableFont.NO_BOLD, + false, + UnderlineStyle.SINGLE); + cf = new WritableCellFormat(underline); + cell.setCellFormat(cf); + + // Change the point size of cell B6 to be 10 point + cell = sheet.getWritableCell(1,5); + WritableFont tenpoint = new WritableFont(WritableFont.ARIAL, 10); + cf = new WritableCellFormat(tenpoint); + cell.setCellFormat(cf); + + // Change the contents of cell B7 to read "Label - mod" + cell = sheet.getWritableCell(1,6); + if (cell.getType() == CellType.LABEL) + { + Label lc = (Label) cell; + lc.setString(lc.getString() + " - mod"); + } + + // Change cell B10 to display 7 dps + cell = sheet.getWritableCell(1,9); + NumberFormat sevendps = new NumberFormat("#.0000000"); + cf = new WritableCellFormat(sevendps); + cell.setCellFormat(cf); + + + // Change cell B11 to display in the format 1e4 + cell = sheet.getWritableCell(1,10); + NumberFormat exp4 = new NumberFormat("0.####E0"); + cf = new WritableCellFormat(exp4); + cell.setCellFormat(cf); + + // Change cell B12 to be normal display + cell = sheet.getWritableCell(1,11); + cell.setCellFormat(WritableWorkbook.NORMAL_STYLE); + + // Change the contents of cell B13 to 42 + cell = sheet.getWritableCell(1,12); + if (cell.getType() == CellType.NUMBER) + { + Number n = (Number) cell; + n.setValue(42); + } + + // Add 0.1 to the contents of cell B14 + cell = sheet.getWritableCell(1,13); + if (cell.getType() == CellType.NUMBER) + { + Number n = (Number) cell; + n.setValue(n.getValue() + 0.1); + } + + // Change the date format of cell B17 to be a custom format + cell = sheet.getWritableCell(1,16); + DateFormat df = new DateFormat("dd MMM yyyy HH:mm:ss"); + cf = new WritableCellFormat(df); + cell.setCellFormat(cf); + + // Change the date format of cell B18 to be a standard format + cell = sheet.getWritableCell(1,17); + cf = new WritableCellFormat(DateFormats.FORMAT9); + cell.setCellFormat(cf); + + // Change the date in cell B19 to be 18 Feb 1998, 11:23:28 + cell = sheet.getWritableCell(1,18); + if (cell.getType() == CellType.DATE) + { + DateTime dt = (DateTime) cell; + Calendar cal = Calendar.getInstance(); + cal.set(1998, 1, 18, 11, 23, 28); + Date d = cal.getTime(); + dt.setDate(d); + } + + // Change the value in B23 to be 6.8. This should recalculate the + // formula + cell = sheet.getWritableCell(1,22); + if (cell.getType() == CellType.NUMBER) + { + Number n = (Number) cell; + n.setValue(6.8); + } + + // Change the label in B30. This will have the effect of making + // the original string unreferenced + cell = sheet.getWritableCell(1, 29); + if (cell.getType() == CellType.LABEL) + { + l = (Label) cell; + l.setString("Modified string contents"); + } + // Insert a new row (number 35) + sheet.insertRow(34); + + // Delete row 38 (39 after row has been inserted) + sheet.removeRow(38); + + // Insert a new column (J) + sheet.insertColumn(9); + + // Remove a column (L - M after column has been inserted) + sheet.removeColumn(11); + + // Remove row 44 (contains a hyperlink), and then insert an empty + // row just to keep the numbers consistent + sheet.removeRow(43); + sheet.insertRow(43); + + // Modify the hyperlinks + WritableHyperlink hyperlinks[] = sheet.getWritableHyperlinks(); + + for (int i = 0; i < hyperlinks.length; i++) + { + WritableHyperlink wh = hyperlinks[i]; + if (wh.getColumn() == 1 && wh.getRow() == 39) + { + try + { + // Change the hyperlink that begins in cell B40 to be a different API + wh.setURL(new URL("http://www.andykhan.com/jexcelapi/index.html")); + } + catch (MalformedURLException e) + { + logger.warn(e.toString()); + } + } + else if (wh.getColumn() == 1 && wh.getRow() == 40) + { + wh.setFile(new File("../jexcelapi/docs/overview-summary.html")); + } + else if (wh.getColumn() == 1 && wh.getRow() == 41) + { + wh.setFile(new File("d:/home/jexcelapi/docs/jxl/package-summary.html")); + } + else if (wh.getColumn() == 1 && wh.getRow() == 44) + { + // Remove the hyperlink at B45 + sheet.removeHyperlink(wh); + } + } + + // Change the background of cell F31 from blue to red + WritableCell c = sheet.getWritableCell(5,30); + WritableCellFormat newFormat = new WritableCellFormat(c.getCellFormat()); + newFormat.setBackground(Colour.RED); + c.setCellFormat(newFormat); + + // Modify the contents of the merged cell + l = new Label(0, 49, "Modified merged cells"); + sheet.addCell(l); + + // Modify the chart data + Number n = (Number) sheet.getWritableCell(0, 70); + n.setValue(9); + + n = (Number) sheet.getWritableCell(0, 71); + n.setValue(10); + + n = (Number) sheet.getWritableCell(0, 73); + n.setValue(4); + + // Add in a cross sheet formula + Formula f = new Formula(1, 80, "ROUND(COS(original!B10),2)"); + sheet.addCell(f); + + // Add in a formula from the named cells + f = new Formula(1, 83, "value1+value2"); + sheet.addCell(f); + + // Add in a function formula using named cells + f = new Formula(1, 84, "AVERAGE(value1,value1*4,value2)"); + sheet.addCell(f); + + // Copy sheet 1 to sheet 3 + // w.copySheet(0, "copy", 2); + + // Use the cell deep copy feature + Label label = new Label(0, 88, "Some copied cells", cf); + sheet.addCell(label); + + label = new Label(0,89, "Number from B9"); + sheet.addCell(label); + + WritableCell wc = sheet.getWritableCell(1, 9).copyTo(1,89); + sheet.addCell(wc); + + label = new Label(0, 90, "Label from B4 (modified format)"); + sheet.addCell(label); + + wc = sheet.getWritableCell(1, 3).copyTo(1,90); + sheet.addCell(wc); + + label = new Label(0, 91, "Date from B17"); + sheet.addCell(label); + + wc = sheet.getWritableCell(1, 16).copyTo(1,91); + sheet.addCell(wc); + + label = new Label(0, 92, "Boolean from E16"); + sheet.addCell(label); + + wc = sheet.getWritableCell(4, 15).copyTo(1,92); + sheet.addCell(wc); + + label = new Label(0, 93, "URL from B40"); + sheet.addCell(label); + + wc = sheet.getWritableCell(1, 39).copyTo(1,93); + sheet.addCell(wc); + + // Add some numbers for the formula copy + for (int i = 0 ; i < 6; i++) + { + Number number = new Number(1,94+i, i + 1 + i/8.0); + sheet.addCell(number); + } + + label = new Label(0,100, "Formula from B27"); + sheet.addCell(label); + + wc = sheet.getWritableCell(1, 26).copyTo(1,100); + sheet.addCell(wc); + + label = new Label(0,101, "A brand new formula"); + sheet.addCell(label); + + Formula formula = new Formula(1, 101, "SUM(B94:B96)"); + sheet.addCell(formula); + + label = new Label(0,102, "A copy of it"); + sheet.addCell(label); + + wc = sheet.getWritableCell(1,101).copyTo(1, 102); + sheet.addCell(wc); + + // Remove the second image from the sheet + WritableImage wi = sheet.getImage(1); + sheet.removeImage(wi); + + wi = new WritableImage(1, 116, 2, 9, + new File("resources/littlemoretonhall.png")); + sheet.addImage(wi); + + // Add a list data validations + label = new Label(0, 151, "Added drop down validation"); + sheet.addCell(label); + + Blank b = new Blank(1, 151); + wcf = new WritableCellFeatures(); + ArrayList al = new ArrayList(); + al.add("The Fellowship of the Ring"); + al.add("The Two Towers"); + al.add("The Return of the King"); + wcf.setDataValidationList(al); + b.setCellFeatures(wcf); + sheet.addCell(b); + + // Add a number data validation + label = new Label(0, 152, "Added number validation 2.718 < x < 3.142"); + sheet.addCell(label); + b = new Blank(1,152); + wcf = new WritableCellFeatures(); + wcf.setNumberValidation(2.718, 3.142, wcf.BETWEEN); + b.setCellFeatures(wcf); + sheet.addCell(b); + + // Modify the text in the first cell with a comment + cell = sheet.getWritableCell(0, 156); + l = (Label) cell; + l.setString("Label text modified"); + + cell = sheet.getWritableCell(0, 157); + wcf = cell.getWritableCellFeatures(); + wcf.setComment("modified comment text"); + + cell = sheet.getWritableCell(0, 158); + wcf = cell.getWritableCellFeatures(); + wcf.removeComment(); + } +} + + + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/demo/Write.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/demo/Write.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/demo/Write.java 17 Aug 2012 14:51:17 -0000 1.1 @@ -0,0 +1,1713 @@ +/********************************************************************* +* +* Copyright (C) 2001 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.demo; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; +import java.util.TimeZone; + +import jxl.CellReferenceHelper; +import jxl.CellView; +import jxl.HeaderFooter; +import jxl.Range; +import jxl.Workbook; +import jxl.WorkbookSettings; +import jxl.format.Alignment; +import jxl.format.Border; +import jxl.format.BorderLineStyle; +import jxl.format.Colour; +import jxl.format.Orientation; +import jxl.format.PageOrder; +import jxl.format.PageOrientation; +import jxl.format.PaperSize; +import jxl.format.ScriptStyle; +import jxl.format.UnderlineStyle; +import jxl.write.Blank; +import jxl.write.Boolean; +import jxl.write.DateFormat; +import jxl.write.DateFormats; +import jxl.write.DateTime; +import jxl.write.Formula; +import jxl.write.Label; +import jxl.write.Number; +import jxl.write.NumberFormat; +import jxl.write.NumberFormats; +import jxl.write.WritableCellFeatures; +import jxl.write.WritableCellFormat; +import jxl.write.WritableFont; +import jxl.write.WritableHyperlink; +import jxl.write.WritableImage; +import jxl.write.WritableSheet; +import jxl.write.WritableWorkbook; +import jxl.write.WriteException; + + +/** + * Demo class which writes a spreadsheet. This demo illustrates most of the + * features of the JExcelAPI, such as text, numbers, fonts, number formats and + * date formats + */ +public class Write +{ + /** + * The filename + */ + private String filename; + + /** + * The workbook + */ + private WritableWorkbook workbook; + + /** + * Constructor + * + * @param fn + */ + public Write(String fn) + { + filename = fn; + } + + /** + * Uses the JExcelAPI to create a spreadsheet + * + * @exception IOException + * @exception WriteException + */ + public void write() throws IOException, WriteException + { + WorkbookSettings ws = new WorkbookSettings(); + ws.setLocale(new Locale("en", "EN")); + workbook = Workbook.createWorkbook(new File(filename), ws); + + + WritableSheet s2 = workbook.createSheet("Number Formats", 0); + WritableSheet s3 = workbook.createSheet("Date Formats", 1); + WritableSheet s1 = workbook.createSheet("Label Formats", 2); + WritableSheet s4 = workbook.createSheet("Borders", 3); + WritableSheet s5 = workbook.createSheet("Labels", 4); + WritableSheet s6 = workbook.createSheet("Formulas", 5); + WritableSheet s7 = workbook.createSheet("Images", 6); + // WritableSheet s8 = workbook.createSheet + // ("'Illegal chars in name !*%^?': which exceeds max name length",7); + + // Modify the colour palette to bright red for the lime colour + workbook.setColourRGB(Colour.LIME, 0xff, 0, 0); + + // Add a named range to the workbook + workbook.addNameArea("namedrange", s4, 1, 11, 5, 14); + workbook.addNameArea("validation_range", s1, 4, 65, 9, 65); + + // Add a print area to the "Labels" sheet + s5.getSettings().setPrintArea(4,4,15,35); + + writeLabelFormatSheet(s1); + writeNumberFormatSheet(s2); + writeDateFormatSheet(s3); + writeBordersSheet(s4); + writeLabelsSheet(s5); + writeFormulaSheet(s6); + writeImageSheet(s7); + + workbook.write(); + workbook.close(); + } + + /** + * Writes out a sheet containing the various numerical formats + * + * @param s + */ + private void writeNumberFormatSheet(WritableSheet s) throws WriteException + { + WritableCellFormat wrappedText = new WritableCellFormat + (WritableWorkbook.ARIAL_10_PT); + wrappedText.setWrap(true); + + s.setColumnView(0,20); + s.setColumnView(4,20); + s.setColumnView(5,20); + s.setColumnView(6,20); + + // Floats + Label l = new Label(0,0,"+/- Pi - default format", wrappedText); + s.addCell(l); + + Number n = new Number(1,0,3.1415926535); + s.addCell(n); + + n = new Number(2,0,-3.1415926535); + s.addCell(n); + + l = new Label(0,1,"+/- Pi - integer format", wrappedText); + s.addCell(l); + + WritableCellFormat cf1 = new WritableCellFormat(NumberFormats.INTEGER); + n = new Number(1,1,3.1415926535,cf1); + s.addCell(n); + + n = new Number(2,1,-3.1415926535, cf1); + s.addCell(n); + + l = new Label(0,2,"+/- Pi - float 2dps", wrappedText); + s.addCell(l); + + WritableCellFormat cf2 = new WritableCellFormat(NumberFormats.FLOAT); + n = new Number(1,2,3.1415926535,cf2); + s.addCell(n); + + n = new Number(2,2,-3.1415926535, cf2); + s.addCell(n); + + l = new Label(0,3,"+/- Pi - custom 3dps", + wrappedText); + s.addCell(l); + + NumberFormat dp3 = new NumberFormat("#.###"); + WritableCellFormat dp3cell = new WritableCellFormat(dp3); + n = new Number(1,3,3.1415926535,dp3cell); + s.addCell(n); + + n = new Number(2,3,-3.1415926535, dp3cell); + s.addCell(n); + + l = new Label(0,4,"+/- Pi - custom &3.14", + wrappedText); + s.addCell(l); + + NumberFormat pounddp2 = new NumberFormat("&#.00"); + WritableCellFormat pounddp2cell = new WritableCellFormat(pounddp2); + n = new Number(1,4,3.1415926535,pounddp2cell); + s.addCell(n); + + n = new Number(2,4,-3.1415926535, pounddp2cell); + s.addCell(n); + + l = new Label(0,5,"+/- Pi - custom Text #.### Text", + wrappedText); + s.addCell(l); + + NumberFormat textdp4 = new NumberFormat("Text#.####Text"); + WritableCellFormat textdp4cell = new WritableCellFormat(textdp4); + n = new Number(1,5,3.1415926535, textdp4cell); + s.addCell(n); + + n = new Number(2,5,-3.1415926535, textdp4cell); + s.addCell(n); + + // Integers + l = new Label(4,0,"+/- Bilko default format"); + s.addCell(l); + n = new Number(5, 0, 15042699); + s.addCell(n); + n = new Number(6, 0, -15042699); + s.addCell(n); + + l = new Label(4,1,"+/- Bilko float format"); + s.addCell(l); + WritableCellFormat cfi1 = new WritableCellFormat(NumberFormats.FLOAT); + n = new Number(5, 1, 15042699, cfi1); + s.addCell(n); + n = new Number(6, 1, -15042699, cfi1); + s.addCell(n); + + l = new Label(4,2,"+/- Thousands separator"); + s.addCell(l); + WritableCellFormat cfi2 = new WritableCellFormat + (NumberFormats.THOUSANDS_INTEGER); + n = new Number(5, 2, 15042699,cfi2 ); + s.addCell(n); + n = new Number(6, 2, -15042699, cfi2); + s.addCell(n); + + l = new Label(4,3,"+/- Accounting red - added 0.01"); + s.addCell(l); + WritableCellFormat cfi3 = new WritableCellFormat + (NumberFormats.ACCOUNTING_RED_FLOAT); + n = new Number(5, 3, 15042699.01, cfi3); + s.addCell(n); + n = new Number(6, 3, -15042699.01, cfi3); + s.addCell(n); + + l = new Label(4,4,"+/- Percent"); + s.addCell(l); + WritableCellFormat cfi4 = new WritableCellFormat + (NumberFormats.PERCENT_INTEGER); + n = new Number(5, 4, 15042699, cfi4); + s.addCell(n); + n = new Number(6, 4, -15042699, cfi4); + s.addCell(n); + + l = new Label(4,5,"+/- Exponential - 2dps"); + s.addCell(l); + WritableCellFormat cfi5 = new WritableCellFormat + (NumberFormats.EXPONENTIAL); + n = new Number(5, 5, 15042699, cfi5); + s.addCell(n); + n = new Number(6, 5, -15042699, cfi5); + s.addCell(n); + + l = new Label(4,6,"+/- Custom exponentional - 3dps", wrappedText); + s.addCell(l); + NumberFormat edp3 = new NumberFormat("0.000E0"); + WritableCellFormat edp3Cell = new WritableCellFormat(edp3); + n = new Number(5,6,15042699,edp3Cell); + s.addCell(n); + n = new Number(6,6,-15042699,edp3Cell); + s.addCell(n); + + l = new Label(4, 7, "Custom neg brackets", wrappedText); + s.addCell(l); + NumberFormat negbracks = new NumberFormat("#,##0;(#,##0)"); + WritableCellFormat negbrackscell = new WritableCellFormat(negbracks); + n = new Number(5,7, 15042699, negbrackscell); + s.addCell(n); + n = new Number(6,7, -15042699, negbrackscell); + s.addCell(n); + + l = new Label(4, 8, "Custom neg brackets 2", wrappedText); + s.addCell(l); + NumberFormat negbracks2 = new NumberFormat("#,##0;(#,##0)a"); + WritableCellFormat negbrackscell2 = new WritableCellFormat(negbracks2); + n = new Number(5,8, 15042699, negbrackscell2); + s.addCell(n); + n = new Number(6,8, -15042699, negbrackscell2); + s.addCell(n); + + l = new Label(4, 9, "Custom percent", wrappedText); + s.addCell(l); + NumberFormat cuspercent = new NumberFormat("0.0%"); + WritableCellFormat cuspercentf = new WritableCellFormat(cuspercent); + n = new Number(5, 9, 3.14159265, cuspercentf); + s.addCell(n); + + + // Booleans + l = new Label(0,10, "Boolean - TRUE"); + s.addCell(l); + Boolean b = new Boolean(1,10, true); + s.addCell(b); + + l = new Label(0,11, "Boolean - FALSE"); + s.addCell(l); + b = new Boolean(1,11,false); + s.addCell(b); + + l = new Label(0, 12, "A hidden cell->"); + s.addCell(l); + n = new Number(1, 12, 17, WritableWorkbook.HIDDEN_STYLE); + s.addCell(n); + + // Currencies + l = new Label(4, 19, "Currency formats"); + s.addCell(l); + + l = new Label(4, 21, "UK Pound"); + s.addCell(l); + NumberFormat poundCurrency = + new NumberFormat(NumberFormat.CURRENCY_POUND + " #,###.00", + NumberFormat.COMPLEX_FORMAT); + WritableCellFormat poundFormat = new WritableCellFormat(poundCurrency); + n = new Number(5, 21, 12345, poundFormat); + s.addCell(n); + + l = new Label(4, 22, "Euro 1"); + s.addCell(l); + NumberFormat euroPrefixCurrency = + new NumberFormat(NumberFormat.CURRENCY_EURO_PREFIX + " #,###.00", + NumberFormat.COMPLEX_FORMAT); + WritableCellFormat euroPrefixFormat = + new WritableCellFormat(euroPrefixCurrency); + n = new Number(5, 22, 12345, euroPrefixFormat); + s.addCell(n); + + l = new Label(4, 23, "Euro 2"); + s.addCell(l); + NumberFormat euroSuffixCurrency = + new NumberFormat("#,###.00" + NumberFormat.CURRENCY_EURO_SUFFIX, + NumberFormat.COMPLEX_FORMAT); + WritableCellFormat euroSuffixFormat = + new WritableCellFormat(euroSuffixCurrency); + n = new Number(5, 23, 12345, euroSuffixFormat); + s.addCell(n); + + l = new Label(4, 24, "Dollar"); + s.addCell(l); + NumberFormat dollarCurrency = + new NumberFormat(NumberFormat.CURRENCY_DOLLAR + " #,###.00", + NumberFormat.COMPLEX_FORMAT); + WritableCellFormat dollarFormat = + new WritableCellFormat(dollarCurrency); + n = new Number(5, 24, 12345, dollarFormat); + s.addCell(n); + + l = new Label(4, 25, "Japanese Yen"); + s.addCell(l); + NumberFormat japaneseYenCurrency = + new NumberFormat(NumberFormat.CURRENCY_JAPANESE_YEN + " #,###.00", + NumberFormat.COMPLEX_FORMAT); + WritableCellFormat japaneseYenFormat = + new WritableCellFormat(japaneseYenCurrency); + n = new Number(5, 25, 12345, japaneseYenFormat); + s.addCell(n); + + l = new Label(4, 30, "Fraction formats"); + s.addCell(l); + + l = new Label(4,32, "One digit fraction format", wrappedText); + s.addCell(l); + + WritableCellFormat fraction1digitformat = + new WritableCellFormat(NumberFormats.FRACTION_ONE_DIGIT); + n = new Number(5, 32, 3.18279, fraction1digitformat); + s.addCell(n); + + l = new Label(4,33, "Two digit fraction format", wrappedText); + s.addCell(l); + + WritableCellFormat fraction2digitformat = + new WritableCellFormat(NumberFormats.FRACTION_TWO_DIGITS); + n = new Number(5, 33, 3.18279, fraction2digitformat); + s.addCell(n); + + l = new Label(4,34, "Three digit fraction format (improper)", wrappedText); + s.addCell(l); + + NumberFormat fraction3digit1 = + new NumberFormat(NumberFormat.FRACTION_THREE_DIGITS, + NumberFormat.COMPLEX_FORMAT); + WritableCellFormat fraction3digitformat1 = + new WritableCellFormat(fraction3digit1); + n = new Number(5, 34, 3.18927, fraction3digitformat1); + s.addCell(n); + + l = new Label(4,35, "Three digit fraction format (proper)", wrappedText); + s.addCell(l); + + NumberFormat fraction3digit2 = + new NumberFormat("# " + NumberFormat.FRACTION_THREE_DIGITS, + NumberFormat.COMPLEX_FORMAT); + WritableCellFormat fraction3digitformat2 = + new WritableCellFormat(fraction3digit2); + n = new Number(5, 35, 3.18927, fraction3digitformat2); + s.addCell(n); + + // Lots of numbers + for (int row = 0; row < 100; row++) + { + for (int col = 8; col < 108; col++) + { + n = new Number(col, row, col+row); + s.addCell(n); + } + } + + // Lots of numbers + for (int row = 101; row < 3000; row++) + { + for (int col = 0; col < 25; col++) + { + n = new Number(col, row, col+row); + s.addCell(n); + } + } + } + + /** + * Adds cells to the specified sheet which test the various date formats + * + * @param s + */ + private void writeDateFormatSheet(WritableSheet s) throws WriteException + { + WritableCellFormat wrappedText = new WritableCellFormat + (WritableWorkbook.ARIAL_10_PT); + wrappedText.setWrap(true); + + s.setColumnView(0, 20); + s.setColumnView(2, 20); + s.setColumnView(3, 20); + s.setColumnView(4, 20); + + s.getSettings().setFitWidth(2); + s.getSettings().setFitHeight(2); + + Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT")); + c.set(1975, 4, 31, 15, 21, 45); + c.set(Calendar.MILLISECOND, 660); + Date date = c.getTime(); + c.set(1900, 0, 1, 0, 0, 0); + c.set(Calendar.MILLISECOND, 0); + + Date date2 = c.getTime(); + c.set(1970, 0, 1, 0, 0, 0); + Date date3 = c.getTime(); + c.set(1918, 10, 11, 11, 0, 0); + Date date4 = c.getTime(); + c.set(1900, 0, 2, 0, 0, 0); + Date date5 = c.getTime(); + c.set(1901, 0, 1, 0, 0, 0); + Date date6 = c.getTime(); + c.set(1900, 4, 31, 0, 0, 0); + Date date7 = c.getTime(); + c.set(1900, 1, 1, 0, 0, 0); + Date date8 = c.getTime(); + c.set(1900, 0, 31, 0, 0, 0); + Date date9 = c.getTime(); + c.set(1900, 2, 1, 0, 0, 0); + Date date10 = c.getTime(); + c.set(1900, 1, 27, 0, 0, 0); + Date date11 = c.getTime(); + c.set(1900, 1, 28, 0, 0, 0); + Date date12 = c.getTime(); + c.set(1980, 5, 31, 12, 0, 0); + Date date13 = c.getTime(); + c.set(1066, 9, 14, 0, 0, 0); + Date date14 = c.getTime(); + + // Built in date formats + SimpleDateFormat sdf = new SimpleDateFormat("dd MMM yyyy HH:mm:ss.SSS"); + sdf.setTimeZone(TimeZone.getTimeZone("GMT")); + Label l = new Label(0,0,"All dates are " + sdf.format(date), + wrappedText); + s.addCell(l); + + l = new Label(0,1,"Built in formats", + wrappedText); + s.addCell(l); + + l = new Label(2, 1, "Custom formats"); + s.addCell(l); + + WritableCellFormat cf1 = new WritableCellFormat(DateFormats.FORMAT1); + DateTime dt = new DateTime(0,2,date, cf1, DateTime.GMT); + s.addCell(dt); + + cf1 = new WritableCellFormat(DateFormats.FORMAT2); + dt = new DateTime(0,3,date, cf1,DateTime.GMT); + s.addCell(dt); + + cf1 = new WritableCellFormat(DateFormats.FORMAT3); + dt = new DateTime(0,4,date, cf1); + s.addCell(dt); + + cf1 = new WritableCellFormat(DateFormats.FORMAT4); + dt = new DateTime(0,5,date, cf1); + s.addCell(dt); + + cf1 = new WritableCellFormat(DateFormats.FORMAT5); + dt = new DateTime(0,6,date, cf1); + s.addCell(dt); + + cf1 = new WritableCellFormat(DateFormats.FORMAT6); + dt = new DateTime(0,7,date, cf1); + s.addCell(dt); + + cf1 = new WritableCellFormat(DateFormats.FORMAT7); + dt = new DateTime(0,8,date, cf1, DateTime.GMT); + s.addCell(dt); + + cf1 = new WritableCellFormat(DateFormats.FORMAT8); + dt = new DateTime(0,9,date, cf1, DateTime.GMT); + s.addCell(dt); + + cf1 = new WritableCellFormat(DateFormats.FORMAT9); + dt = new DateTime(0,10,date, cf1, DateTime.GMT); + s.addCell(dt); + + cf1 = new WritableCellFormat(DateFormats.FORMAT10); + dt = new DateTime(0,11,date, cf1, DateTime.GMT); + s.addCell(dt); + + cf1 = new WritableCellFormat(DateFormats.FORMAT11); + dt = new DateTime(0,12,date, cf1, DateTime.GMT); + s.addCell(dt); + + cf1 = new WritableCellFormat(DateFormats.FORMAT12); + dt = new DateTime(0,13,date, cf1, DateTime.GMT); + s.addCell(dt); + + // Custom formats + DateFormat df = new DateFormat("dd MM yyyy"); + cf1 = new WritableCellFormat(df); + l = new Label(2, 2, "dd MM yyyy"); + s.addCell(l); + + dt = new DateTime(3, 2, date, cf1, DateTime.GMT); + s.addCell(dt); + + df = new DateFormat("dd MMM yyyy"); + cf1 = new WritableCellFormat(df); + l = new Label(2, 3, "dd MMM yyyy"); + s.addCell(l); + + dt = new DateTime(3, 3, date, cf1, DateTime.GMT); + s.addCell(dt); + + df = new DateFormat("hh:mm"); + cf1 = new WritableCellFormat(df); + l = new Label(2, 4, "hh:mm"); + s.addCell(l); + + dt = new DateTime(3, 4, date, cf1, DateTime.GMT); + s.addCell(dt); + + df = new DateFormat("hh:mm:ss"); + cf1 = new WritableCellFormat(df); + l = new Label(2, 5, "hh:mm:ss"); + s.addCell(l); + + dt = new DateTime(3, 5, date, cf1, DateTime.GMT); + s.addCell(dt); + + df = new DateFormat("H:mm:ss a"); + cf1 = new WritableCellFormat(df); + l = new Label(2, 5, "H:mm:ss a"); + s.addCell(l); + + dt = new DateTime(3, 5, date, cf1, DateTime.GMT); + s.addCell(dt); + dt = new DateTime(4, 5, date13, cf1, DateTime.GMT); + s.addCell(dt); + + df = new DateFormat("mm:ss.SSS"); + cf1 = new WritableCellFormat(df); + l = new Label(2, 6, "mm:ss.SSS"); + s.addCell(l); + + dt = new DateTime(3, 6, date, cf1, DateTime.GMT); + s.addCell(dt); + + df = new DateFormat("hh:mm:ss a"); + cf1 = new WritableCellFormat(df); + l = new Label(2, 7, "hh:mm:ss a"); + s.addCell(l); + + dt = new DateTime(4, 7, date13, cf1, DateTime.GMT); + s.addCell(dt); + + + // Check out the zero date ie. 1 Jan 1900 + l = new Label(0,16,"Zero date " + sdf.format(date2), + wrappedText); + s.addCell(l); + + cf1 = new WritableCellFormat(DateFormats.FORMAT9); + dt = new DateTime(0,17,date2, cf1, DateTime.GMT); + s.addCell(dt); + + // Check out the zero date + 1 ie. 2 Jan 1900 + l = new Label(3,16,"Zero date + 1 " + sdf.format(date5), + wrappedText); + s.addCell(l); + + cf1 = new WritableCellFormat(DateFormats.FORMAT9); + dt = new DateTime(3,17,date5, cf1, DateTime.GMT); + s.addCell(dt); + + // Check out the 1 Jan 1901 + l = new Label(3,19, sdf.format(date6), + wrappedText); + s.addCell(l); + + cf1 = new WritableCellFormat(DateFormats.FORMAT9); + dt = new DateTime(3,20,date6, cf1, DateTime.GMT); + s.addCell(dt); + + // Check out the 31 May 1900 + l = new Label(3,22, sdf.format(date7), + wrappedText); + s.addCell(l); + + cf1 = new WritableCellFormat(DateFormats.FORMAT9); + dt = new DateTime(3,23, date7, cf1, DateTime.GMT); + s.addCell(dt); + + // Check out 1 Feb 1900 + l = new Label(3,25, sdf.format(date8), + wrappedText); + s.addCell(l); + + cf1 = new WritableCellFormat(DateFormats.FORMAT9); + dt = new DateTime(3,26, date8, cf1, DateTime.GMT); + s.addCell(dt); + + // Check out 31 Jan 1900 + l = new Label(3,28, sdf.format(date9), + wrappedText); + s.addCell(l); + + cf1 = new WritableCellFormat(DateFormats.FORMAT9); + dt = new DateTime(3,29, date9, cf1, DateTime.GMT); + s.addCell(dt); + + // Check out 31 Jan 1900 + l = new Label(3,28, sdf.format(date9), + wrappedText); + s.addCell(l); + + cf1 = new WritableCellFormat(DateFormats.FORMAT9); + dt = new DateTime(3,29, date9, cf1, DateTime.GMT); + s.addCell(dt); + + // Check out 1 Mar 1900 + l = new Label(3,31, sdf.format(date10), + wrappedText); + s.addCell(l); + + cf1 = new WritableCellFormat(DateFormats.FORMAT9); + dt = new DateTime(3,32, date10, cf1, DateTime.GMT); + s.addCell(dt); + + // Check out 27 Feb 1900 + l = new Label(3,34, sdf.format(date11), + wrappedText); + s.addCell(l); + + cf1 = new WritableCellFormat(DateFormats.FORMAT9); + dt = new DateTime(3,35, date11, cf1, DateTime.GMT); + s.addCell(dt); + + // Check out 28 Feb 1900 + l = new Label(3,37, sdf.format(date12), + wrappedText); + s.addCell(l); + + cf1 = new WritableCellFormat(DateFormats.FORMAT9); + dt = new DateTime(3,38, date12, cf1, DateTime.GMT); + s.addCell(dt); + + // Check out the zero date ie. 1 Jan 1970 + l = new Label(0,19,"Zero UTC date " + sdf.format(date3), + wrappedText); + s.addCell(l); + + cf1 = new WritableCellFormat(DateFormats.FORMAT9); + dt = new DateTime(0,20,date3, cf1, DateTime.GMT); + s.addCell(dt); + + // Check out the WWI armistice day ie. 11 am, Nov 11, 1918 + l = new Label(0,22,"Armistice date " + sdf.format(date4), + wrappedText); + s.addCell(l); + + cf1 = new WritableCellFormat(DateFormats.FORMAT9); + dt = new DateTime(0,23,date4, cf1, DateTime.GMT); + s.addCell(dt); + + // Check out the Battle of Hastings date Oct 14th, 1066 + l = new Label(0,25, "Battle of Hastings " + sdf.format(date14), + wrappedText); + s.addCell(l); + + cf1 = new WritableCellFormat(DateFormats.FORMAT2); + dt = new DateTime(0, 26, date14, cf1, DateTime.GMT); + s.addCell(dt); + } + + /** + * Adds cells to the specified sheet which test the various label formatting + * styles, such as different fonts, different sizes and bold, underline etc. + * + * @param s1 + */ + private void writeLabelFormatSheet(WritableSheet s1) throws WriteException + { + s1.setColumnView(0, 60); + + Label lr = new Label(0,0, "Arial Fonts"); + s1.addCell(lr); + + lr = new Label(1,0, "10pt"); + s1.addCell(lr); + + lr = new Label(2, 0, "Normal"); + s1.addCell(lr); + + lr = new Label(3, 0, "12pt"); + s1.addCell(lr); + + WritableFont arial12pt = new WritableFont(WritableFont.ARIAL, 12); + WritableCellFormat arial12format = new WritableCellFormat(arial12pt); + arial12format.setWrap(true); + lr = new Label(4, 0, "Normal", arial12format); + s1.addCell(lr); + + WritableFont arial10ptBold = new WritableFont + (WritableFont.ARIAL, 10, WritableFont.BOLD); + WritableCellFormat arial10BoldFormat = new WritableCellFormat + (arial10ptBold); + lr = new Label(2, 2, "BOLD", arial10BoldFormat); + s1.addCell(lr); + + WritableFont arial12ptBold = new WritableFont + (WritableFont.ARIAL, 12, WritableFont.BOLD); + WritableCellFormat arial12BoldFormat = new WritableCellFormat + (arial12ptBold); + lr = new Label(4, 2, "BOLD", arial12BoldFormat); + s1.addCell(lr); + + WritableFont arial10ptItalic = new WritableFont + (WritableFont.ARIAL, 10, WritableFont.NO_BOLD, true); + WritableCellFormat arial10ItalicFormat = new WritableCellFormat + (arial10ptItalic); + lr = new Label(2, 4, "Italic", arial10ItalicFormat); + s1.addCell(lr); + + WritableFont arial12ptItalic = new WritableFont + (WritableFont.ARIAL, 12, WritableFont.NO_BOLD, true); + WritableCellFormat arial12ptItalicFormat = new WritableCellFormat + (arial12ptItalic); + lr = new Label(4, 4, "Italic", arial12ptItalicFormat); + s1.addCell(lr); + + WritableFont times10pt = new WritableFont(WritableFont.TIMES, 10); + WritableCellFormat times10format = new WritableCellFormat(times10pt); + lr = new Label(0, 7, "Times Fonts", times10format); + s1.addCell(lr); + + lr = new Label(1, 7, "10pt", times10format); + s1.addCell(lr); + + lr = new Label(2, 7, "Normal", times10format); + s1.addCell(lr); + + lr = new Label(3, 7, "12pt", times10format); + s1.addCell(lr); + + WritableFont times12pt = new WritableFont(WritableFont.TIMES, 12); + WritableCellFormat times12format = new WritableCellFormat(times12pt); + lr = new Label(4, 7, "Normal", times12format); + s1.addCell(lr); + + WritableFont times10ptBold = new WritableFont + (WritableFont.TIMES, 10, WritableFont.BOLD); + WritableCellFormat times10BoldFormat = new WritableCellFormat + (times10ptBold); + lr = new Label(2, 9, "BOLD", times10BoldFormat); + s1.addCell(lr); + + WritableFont times12ptBold = new WritableFont + (WritableFont.TIMES, 12, WritableFont.BOLD); + WritableCellFormat times12BoldFormat = new WritableCellFormat + (times12ptBold); + lr = new Label(4, 9, "BOLD", times12BoldFormat); + s1.addCell(lr); + + // The underline styles + s1.setColumnView(6, 22); + s1.setColumnView(7, 22); + s1.setColumnView(8, 22); + s1.setColumnView(9, 22); + + lr = new Label(0, 11, "Underlining"); + s1.addCell(lr); + + WritableFont arial10ptUnderline = new WritableFont + (WritableFont.ARIAL, + WritableFont.DEFAULT_POINT_SIZE, + WritableFont.NO_BOLD, + false, + UnderlineStyle.SINGLE); + WritableCellFormat arialUnderline = new WritableCellFormat + (arial10ptUnderline); + lr = new Label(6,11, "Underline", arialUnderline); + s1.addCell(lr); + + WritableFont arial10ptDoubleUnderline = new WritableFont + (WritableFont.ARIAL, + WritableFont.DEFAULT_POINT_SIZE, + WritableFont.NO_BOLD, + false, + UnderlineStyle.DOUBLE); + WritableCellFormat arialDoubleUnderline = new WritableCellFormat + (arial10ptDoubleUnderline); + lr = new Label(7,11, "Double Underline", arialDoubleUnderline); + s1.addCell(lr); + + WritableFont arial10ptSingleAcc = new WritableFont + (WritableFont.ARIAL, + WritableFont.DEFAULT_POINT_SIZE, + WritableFont.NO_BOLD, + false, + UnderlineStyle.SINGLE_ACCOUNTING); + WritableCellFormat arialSingleAcc = new WritableCellFormat + (arial10ptSingleAcc); + lr = new Label(8,11, "Single Accounting Underline", arialSingleAcc); + s1.addCell(lr); + + WritableFont arial10ptDoubleAcc = new WritableFont + (WritableFont.ARIAL, + WritableFont.DEFAULT_POINT_SIZE, + WritableFont.NO_BOLD, + false, + UnderlineStyle.DOUBLE_ACCOUNTING); + WritableCellFormat arialDoubleAcc = new WritableCellFormat + (arial10ptDoubleAcc); + lr = new Label(9,11, "Double Accounting Underline", arialDoubleAcc); + s1.addCell(lr); + + WritableFont times14ptBoldUnderline = new WritableFont + (WritableFont.TIMES, + 14, + WritableFont.BOLD, + false, + UnderlineStyle.SINGLE); + WritableCellFormat timesBoldUnderline = new WritableCellFormat + (times14ptBoldUnderline); + lr = new Label(6,12, "Times 14 Bold Underline", timesBoldUnderline); + s1.addCell(lr); + + WritableFont arial18ptBoldItalicUnderline = new WritableFont + (WritableFont.ARIAL, + 18, + WritableFont.BOLD, + true, + UnderlineStyle.SINGLE); + WritableCellFormat arialBoldItalicUnderline = new WritableCellFormat + (arial18ptBoldItalicUnderline); + lr = new Label(6,13, "Arial 18 Bold Italic Underline", + arialBoldItalicUnderline); + s1.addCell(lr); + + lr = new Label(0, 15, "Script styles"); + s1.addCell(lr); + + WritableFont superscript = new WritableFont + (WritableFont.ARIAL, + WritableFont.DEFAULT_POINT_SIZE, + WritableFont.NO_BOLD, + false, + UnderlineStyle.NO_UNDERLINE, + Colour.BLACK, + ScriptStyle.SUPERSCRIPT); + WritableCellFormat superscriptFormat = new WritableCellFormat + (superscript); + lr = new Label(1,15, "superscript", superscriptFormat); + s1.addCell(lr); + + WritableFont subscript = new WritableFont + (WritableFont.ARIAL, + WritableFont.DEFAULT_POINT_SIZE, + WritableFont.NO_BOLD, + false, + UnderlineStyle.NO_UNDERLINE, + Colour.BLACK, + ScriptStyle.SUBSCRIPT); + WritableCellFormat subscriptFormat = new WritableCellFormat + (subscript); + lr = new Label(2,15, "subscript", subscriptFormat); + s1.addCell(lr); + + lr = new Label(0, 17, "Colours"); + s1.addCell(lr); + + WritableFont red = new WritableFont(WritableFont.ARIAL, + WritableFont.DEFAULT_POINT_SIZE, + WritableFont.NO_BOLD, + false, + UnderlineStyle.NO_UNDERLINE, + Colour.RED); + WritableCellFormat redFormat = new WritableCellFormat(red); + lr = new Label(2, 17, "Red", redFormat); + s1.addCell(lr); + + WritableFont blue = new WritableFont(WritableFont.ARIAL, + WritableFont.DEFAULT_POINT_SIZE, + WritableFont.NO_BOLD, + false, + UnderlineStyle.NO_UNDERLINE, + Colour.BLUE); + WritableCellFormat blueFormat = new WritableCellFormat(blue); + lr = new Label(2, 18, "Blue", blueFormat); + s1.addCell(lr); + + WritableFont lime = new WritableFont(WritableFont.ARIAL); + lime.setColour(Colour.LIME); + WritableCellFormat limeFormat = new WritableCellFormat(lime); + limeFormat.setWrap(true); + lr = new Label(4, 18, "Modified palette - was lime, now red", limeFormat); + s1.addCell(lr); + + WritableCellFormat greyBackground = new WritableCellFormat(); + greyBackground.setWrap(true); + greyBackground.setBackground(Colour.GRAY_50); + lr = new Label(2, 19, "Grey background", greyBackground); + s1.addCell(lr); + + WritableFont yellow = new WritableFont(WritableFont.ARIAL, + WritableFont.DEFAULT_POINT_SIZE, + WritableFont.NO_BOLD, + false, + UnderlineStyle.NO_UNDERLINE, + Colour.YELLOW); + WritableCellFormat yellowOnBlue = new WritableCellFormat(yellow); + yellowOnBlue.setWrap(true); + yellowOnBlue.setBackground(Colour.BLUE); + lr = new Label(2, 20, "Blue background, yellow foreground", yellowOnBlue); + s1.addCell(lr); + + WritableCellFormat yellowOnBlack = new WritableCellFormat(yellow); + yellowOnBlack.setWrap(true); + yellowOnBlack.setBackground(Colour.PALETTE_BLACK); + lr = new Label(3, 20, "Black background, yellow foreground", + yellowOnBlack); + s1.addCell(lr); + + lr = new Label(0, 22, "Null label"); + s1.addCell(lr); + + lr = new Label(2, 22, null); + s1.addCell(lr); + + lr = new Label(0, 24, + "A very long label, more than 255 characters\012" + + "Rejoice O shores\012" + + "Sing O bells\012" + + "But I with mournful tread\012" + + "Walk the deck my captain lies\012" + + "Fallen cold and dead\012"+ + "Summer surprised, coming over the Starnbergersee\012" + + "With a shower of rain. We stopped in the Colonnade\012" + + "A very long label, more than 255 characters\012" + + "Rejoice O shores\012" + + "Sing O bells\012" + + "But I with mournful tread\012" + + "Walk the deck my captain lies\012" + + "Fallen cold and dead\012"+ + "Summer surprised, coming over the Starnbergersee\012" + + "With a shower of rain. We stopped in the Colonnade\012" + "A very long label, more than 255 characters\012" + + "Rejoice O shores\012" + + "Sing O bells\012" + + "But I with mournful tread\012" + + "Walk the deck my captain lies\012" + + "Fallen cold and dead\012"+ + "Summer surprised, coming over the Starnbergersee\012" + + "With a shower of rain. We stopped in the Colonnade\012" + "A very long label, more than 255 characters\012" + + "Rejoice O shores\012" + + "Sing O bells\012" + + "But I with mournful tread\012" + + "Walk the deck my captain lies\012" + + "Fallen cold and dead\012"+ + "Summer surprised, coming over the Starnbergersee\012" + + "With a shower of rain. We stopped in the Colonnade\012" + + "And sat and drank coffee an talked for an hour\012", + arial12format); + s1.addCell(lr); + + WritableCellFormat vertical = new WritableCellFormat(); + vertical.setOrientation(Orientation.VERTICAL); + lr = new Label(0, 26, "Vertical orientation", vertical); + s1.addCell(lr); + + + WritableCellFormat plus_90 = new WritableCellFormat(); + plus_90.setOrientation(Orientation.PLUS_90); + lr = new Label(1, 26, "Plus 90", plus_90); + s1.addCell(lr); + + + WritableCellFormat minus_90 = new WritableCellFormat(); + minus_90.setOrientation(Orientation.MINUS_90); + lr = new Label(2, 26, "Minus 90", minus_90); + s1.addCell(lr); + + lr = new Label(0, 28, "Modified row height"); + s1.addCell(lr); + s1.setRowView(28, 24*20); + + lr = new Label(0, 29, "Collapsed row"); + s1.addCell(lr); + s1.setRowView(29, true); + + // Write hyperlinks + try + { + Label l = new Label(0, 30, "Hyperlink to home page"); + s1.addCell(l); + + URL url = new URL("http://www.andykhan.com/jexcelapi"); + WritableHyperlink wh = new WritableHyperlink(0, 30, 8, 31, url); + s1.addHyperlink(wh); + + // The below hyperlink clashes with above + WritableHyperlink wh2 = new WritableHyperlink(7, 30, 9, 31, url); + s1.addHyperlink(wh2); + + l = new Label(4, 2, "File hyperlink to documentation"); + s1.addCell(l); + + File file = new File("../jexcelapi/docs/index.html"); + wh = new WritableHyperlink(0, 32, 8, 32, file, + "JExcelApi Documentation"); + s1.addHyperlink(wh); + + // Add a hyperlink to another cell on this sheet + wh = new WritableHyperlink(0, 34, 8, 34, + "Link to another cell", + s1, + 0, 180, 1, 181); + s1.addHyperlink(wh); + + file = new File("\\\\localhost\\file.txt"); + wh = new WritableHyperlink(0, 36, 8, 36, file); + s1.addHyperlink(wh); + + // Add a very long hyperlink + url = new URL("http://www.amazon.co.uk/exec/obidos/ASIN/0571058086"+ + "/qid=1099836249/sr=1-3/ref=sr_1_11_3/202-6017285-1620664"); + wh = new WritableHyperlink(0, 38, 0, 38, url); + s1.addHyperlink(wh); + } + catch (MalformedURLException e) + { + System.err.println(e.toString()); + } + + // Write out some merged cells + Label l = new Label(5, 35, "Merged cells", timesBoldUnderline); + s1.mergeCells(5, 35, 8, 37); + s1.addCell(l); + + l = new Label(5, 38, "More merged cells"); + s1.addCell(l); + Range r = s1.mergeCells(5, 38, 8, 41); + s1.insertRow(40); + s1.removeRow(39); + s1.unmergeCells(r); + + // Merge cells and centre across them + WritableCellFormat wcf = new WritableCellFormat(); + wcf.setAlignment(Alignment.CENTRE); + l = new Label(5, 42, "Centred across merged cells", wcf); + s1.addCell(l); + s1.mergeCells(5, 42, 10, 42); + + wcf = new WritableCellFormat(); + wcf.setBorder(Border.ALL, BorderLineStyle.THIN); + wcf.setBackground(Colour.GRAY_25); + l = new Label(3, 44, "Merged with border", wcf); + s1.addCell(l); + s1.mergeCells(3, 44, 4, 46); + + // Clash some ranges - the second range will not be added + // Also merge some cells with two data items in the - the second data + // item will not be merged + /* + l = new Label(5, 16, "merged cells"); + s1.addCell(l); + + Label l5 = new Label(7, 17, "this label won't appear"); + s1.addCell(l5); + s1.mergeCells(5, 16, 8, 18); + + s1.mergeCells(5, 19, 6, 24); + s1.mergeCells(6, 18, 10, 19); + */ + + WritableFont courier10ptFont = new WritableFont(WritableFont.COURIER, 10); + WritableCellFormat courier10pt = new WritableCellFormat(courier10ptFont); + l = new Label(0, 49, "Courier fonts", courier10pt); + s1.addCell(l); + + WritableFont tahoma12ptFont = new WritableFont(WritableFont.TAHOMA, 12); + WritableCellFormat tahoma12pt = new WritableCellFormat(tahoma12ptFont); + l = new Label(0, 50, "Tahoma fonts", tahoma12pt); + s1.addCell(l); + + WritableFont.FontName wingdingsFont = + WritableFont.createFont("Wingdings 2"); + WritableFont wingdings210ptFont = new WritableFont(wingdingsFont, 10); + WritableCellFormat wingdings210pt = new WritableCellFormat + (wingdings210ptFont); + l = new Label(0,51, "Bespoke Windgdings 2", wingdings210pt); + s1.addCell(l); + + WritableCellFormat shrinkToFit = new WritableCellFormat(times12pt); + shrinkToFit.setShrinkToFit(true); + l = new Label(3,53, "Shrunk to fit", shrinkToFit); + s1.addCell(l); + + l = new Label(3,55, "Some long wrapped text in a merged cell", + arial12format); + s1.addCell(l); + s1.mergeCells(3,55,4,55); + + l = new Label(0, 57, "A cell with a comment"); + WritableCellFeatures cellFeatures = new WritableCellFeatures(); + cellFeatures.setComment("the cell comment"); + l.setCellFeatures(cellFeatures); + s1.addCell(l); + + l = new Label(0, 59, + "A cell with a long comment"); + cellFeatures = new WritableCellFeatures(); + cellFeatures.setComment("a very long cell comment indeed that won't " + + "fit inside a standard comment box, so a " + + "larger comment box is used instead", + 5, 6); + l.setCellFeatures(cellFeatures); + s1.addCell(l); + + WritableCellFormat indented = new WritableCellFormat(times12pt); + indented.setIndentation(4); + l = new Label(0, 61, "Some indented text", indented); + s1.addCell(l); + + l = new Label(0, 63, "Data validation: list"); + s1.addCell(l); + + Blank b = new Blank(1,63); + cellFeatures = new WritableCellFeatures(); + ArrayList al = new ArrayList(); + al.add("bagpuss"); + al.add("clangers"); + al.add("ivor the engine"); + al.add("noggin the nog"); + cellFeatures.setDataValidationList(al); + b.setCellFeatures(cellFeatures); + s1.addCell(b); + + l = new Label(0, 64, "Data validation: number > 4.5"); + s1.addCell(l); + + b = new Blank(1,64); + cellFeatures = new WritableCellFeatures(); + cellFeatures.setNumberValidation(4.5, WritableCellFeatures.GREATER_THAN); + b.setCellFeatures(cellFeatures); + s1.addCell(b); + + l = new Label(0, 65, "Data validation: named range"); + s1.addCell(l); + + l = new Label(4, 65, "tiger"); + s1.addCell(l); + l = new Label(5, 65, "sword"); + s1.addCell(l); + l = new Label(6, 65, "honour"); + s1.addCell(l); + l = new Label(7, 65, "company"); + s1.addCell(l); + l = new Label(8, 65, "victory"); + s1.addCell(l); + l = new Label(9, 65, "fortress"); + s1.addCell(l); + + b = new Blank(1,65); + cellFeatures = new WritableCellFeatures(); + cellFeatures.setDataValidationRange("validation_range"); + b.setCellFeatures(cellFeatures); + s1.addCell(b); + + // Set the row grouping + s1.setRowGroup(39, 45, false); + // s1.setRowGroup(72, 74, true); + } + + /** + * Adds cells to the specified sheet which test the various border + * styles + * + * @param s + */ + private void writeBordersSheet(WritableSheet s) throws WriteException + { + s.getSettings().setProtected(true); + + s.setColumnView(1, 15); + s.setColumnView(2, 15); + s.setColumnView(4, 15); + WritableCellFormat thickLeft = new WritableCellFormat(); + thickLeft.setBorder(Border.LEFT, BorderLineStyle.THICK); + Label lr = new Label(1,0, "Thick left", thickLeft); + s.addCell(lr); + + WritableCellFormat dashedRight = new WritableCellFormat(); + dashedRight.setBorder(Border.RIGHT, BorderLineStyle.DASHED); + lr = new Label(2, 0, "Dashed right", dashedRight); + s.addCell(lr); + + WritableCellFormat doubleTop = new WritableCellFormat(); + doubleTop.setBorder(Border.TOP, BorderLineStyle.DOUBLE); + lr = new Label(1, 2, "Double top", doubleTop); + s.addCell(lr); + + WritableCellFormat hairBottom = new WritableCellFormat(); + hairBottom.setBorder(Border.BOTTOM, BorderLineStyle.HAIR); + lr = new Label(2, 2, "Hair bottom", hairBottom); + s.addCell(lr); + + WritableCellFormat allThin = new WritableCellFormat(); + allThin.setBorder(Border.ALL, BorderLineStyle.THIN); + lr = new Label(4, 2, "All thin", allThin); + s.addCell(lr); + + WritableCellFormat twoBorders = new WritableCellFormat(); + twoBorders.setBorder(Border.TOP, BorderLineStyle.THICK); + twoBorders.setBorder(Border.LEFT, BorderLineStyle.THICK); + lr = new Label(6,2, "Two borders", twoBorders); + s.addCell(lr); + + // Create a cell in the middle of nowhere (out of the grow region) + lr = new Label(20, 20, "Dislocated cell - after a page break"); + s.addCell(lr); + + // Set the orientation and the margins + s.getSettings().setPaperSize(PaperSize.A3); + s.getSettings().setOrientation(PageOrientation.LANDSCAPE); + s.getSettings().setPageOrder(PageOrder.DOWN_THEN_RIGHT); + s.getSettings().setHeaderMargin(2); + s.getSettings().setFooterMargin(2); + + s.getSettings().setTopMargin(3); + s.getSettings().setBottomMargin(3); + + // Add a header and footera + HeaderFooter header = new HeaderFooter(); + header.getCentre().append("Page Header"); + s.getSettings().setHeader(header); + + HeaderFooter footer = new HeaderFooter(); + footer.getRight().append("page "); + footer.getRight().appendPageNumber(); + s.getSettings().setFooter(footer); + + // Add a page break and insert a couple of rows + s.addRowPageBreak(18); + s.insertRow(17); + s.insertRow(17); + s.removeRow(17); + + // Add a page break off the screen + s.addRowPageBreak(30); + + // Add a hidden column + lr = new Label(10, 1, "Hidden column"); + s.addCell(lr); + + lr = new Label(3, 8, "Hidden row"); + s.addCell(lr); + s.setRowView(8, true); + + WritableCellFormat allThickRed = new WritableCellFormat(); + allThickRed.setBorder(Border.ALL, BorderLineStyle.THICK, Colour.RED); + lr = new Label(1, 5, "All thick red", allThickRed); + s.addCell(lr); + + WritableCellFormat topBottomBlue = new WritableCellFormat(); + topBottomBlue.setBorder(Border.TOP, BorderLineStyle.THIN, Colour.BLUE); + topBottomBlue.setBorder(Border.BOTTOM, BorderLineStyle.THIN, Colour.BLUE); + lr = new Label(4, 5, "Top and bottom blue", topBottomBlue); + s.addCell(lr); + } + + /** + * Write out loads of labels, in order to test the shared string table + */ + private void writeLabelsSheet(WritableSheet ws) throws WriteException + { + ws.getSettings().setProtected(true); + ws.getSettings().setPassword("jxl"); + ws.getSettings().setVerticalFreeze(5); + ws.getSettings().setDefaultRowHeight(25*20); + + WritableFont wf = new WritableFont(WritableFont.ARIAL, 12); + wf.setItalic(true); + + WritableCellFormat wcf = new WritableCellFormat(wf); + + CellView cv = new CellView(); + cv.setSize(25 * 256); + cv.setFormat(wcf); + ws.setColumnView(0, cv); + ws.setColumnView(1, 15); + + for (int i = 0; i < 61; i++) + { + Label l1 = new Label(0, i, "Common Label"); + Label l2 = new Label(1, i, "Distinct label number " + i); + ws.addCell(l1); + ws.addCell(l2); + } + + // Frig this test record - it appears exactly on the boundary of an SST + // continue record + + Label l3 = new Label(0, 61, "Common Label", wcf); + Label l4 = new Label(1, 61, "1-1234567890", wcf); + Label l5 = new Label(2, 61, "2-1234567890", wcf); + ws.addCell(l3); + ws.addCell(l4); + ws.addCell(l5); + + for (int i = 62; i < 200; i++) + { + Label l1 = new Label(0, i, "Common Label"); + Label l2 = new Label(1, i, "Distinct label number " + i); + ws.addCell(l1); + ws.addCell(l2); + } + + // Add in a last label which doesn't take the common format + wf = new WritableFont(WritableFont.TIMES, 10, WritableFont.BOLD); + wf.setColour(Colour.RED); + WritableCellFormat wcf2 = new WritableCellFormat(wf); + wcf2.setWrap(true); + Label l = new Label(0, 205, "Different format", wcf2); + ws.addCell(l); + + // Add some labels to column 5 for autosizing + Label l6 = new Label(5, 2, "A column for autosizing", wcf2); + ws.addCell(l6); + l6 = new Label(5, 4, "Another label, longer this time and " + + "in a different font"); + ws.addCell(l6); + + CellView cf = new CellView(); + cf.setAutosize(true); + ws.setColumnView(5, cf); + } + + /** + * Test out the formula parser + */ + private void writeFormulaSheet(WritableSheet ws) throws WriteException + { + // Add some cells to manipulate + Number nc = new Number(0,0,15); + ws.addCell(nc); + + nc = new Number(0,1,16); + ws.addCell(nc); + + nc = new Number(0,2,10); + ws.addCell(nc); + + nc = new Number(0,3, 12); + ws.addCell(nc); + + ws.setColumnView(2, 20); + WritableCellFormat wcf = new WritableCellFormat(); + wcf.setAlignment(Alignment.RIGHT); + wcf.setWrap(true); + CellView cv = new CellView(); + cv.setSize(25 * 256); + cv.setFormat(wcf); + ws.setColumnView(3, cv); + + // Add in the formulas + Formula f = null; + Label l = null; + + f = new Formula(2,0, "A1+A2"); + ws.addCell(f); + l = new Label(3, 0, "a1+a2"); + ws.addCell(l); + + f = new Formula(2,1, "A2 * 3"); + ws.addCell(f); + l = new Label(3,1, "A2 * 3"); + ws.addCell(l); + + f = new Formula(2,2, "A2+A1/2.5"); + ws.addCell(f); + l = new Label(3,2, "A2+A1/2.5"); + ws.addCell(l); + + f = new Formula(2,3, "3+(a1+a2)/2.5"); + ws.addCell(f); + l = new Label(3,3, "3+(a1+a2)/2.5"); + ws.addCell(l); + + f = new Formula(2,4, "(a1+a2)/2.5"); + ws.addCell(f); + l = new Label(3,4, "(a1+a2)/2.5"); + ws.addCell(l); + + f = new Formula(2,5, "15+((a1+a2)/2.5)*17"); + ws.addCell(f); + l = new Label(3,5, "15+((a1+a2)/2.5)*17"); + ws.addCell(l); + + f = new Formula(2, 6, "SUM(a1:a4)"); + ws.addCell(f); + l = new Label(3, 6, "SUM(a1:a4)"); + ws.addCell(l); + + f = new Formula(2, 7, "SUM(a1:a4)/4"); + ws.addCell(f); + l = new Label(3, 7, "SUM(a1:a4)/4"); + ws.addCell(l); + + f = new Formula(2, 8, "AVERAGE(A1:A4)"); + ws.addCell(f); + l = new Label(3, 8, "AVERAGE(a1:a4)"); + ws.addCell(l); + + f = new Formula(2, 9, "MIN(5,4,1,2,3)"); + ws.addCell(f); + l = new Label(3, 9, "MIN(5,4,1,2,3)"); + ws.addCell(l); + + f = new Formula(2, 10, "ROUND(3.14159265, 3)"); + ws.addCell(f); + l = new Label(3, 10, "ROUND(3.14159265, 3)"); + ws.addCell(l); + + f = new Formula(2, 11, "MAX(SUM(A1:A2), A1*A2, POWER(A1, 2))"); + ws.addCell(f); + l = new Label(3, 11, "MAX(SUM(A1:A2), A1*A2, POWER(A1, 2))"); + ws.addCell(l); + + f = new Formula(2,12, "IF(A2>A1, \"A2 bigger\", \"A1 bigger\")"); + ws.addCell(f); + l = new Label(3,12, "IF(A2>A1, \"A2 bigger\", \"A1 bigger\")"); + ws.addCell(l); + + f = new Formula(2,13, "IF(A2<=A1, \"A2 smaller\", \"A1 smaller\")"); + ws.addCell(f); + l = new Label(3,13, "IF(A2<=A1, \"A2 smaller\", \"A1 smaller\")"); + ws.addCell(l); + + f = new Formula(2,14, "IF(A3<=10, \"<= 10\")"); + ws.addCell(f); + l = new Label(3,14, "IF(A3<=10, \"<= 10\")"); + ws.addCell(l); + + f = new Formula(2, 15, "SUM(1,2,3,4,5)"); + ws.addCell(f); + l = new Label(3, 15, "SUM(1,2,3,4,5)"); + ws.addCell(l); + + f = new Formula(2, 16, "HYPERLINK(\"http://www.andykhan.com/jexcelapi\", \"JExcelApi Home Page\")"); + ws.addCell(f); + l = new Label(3, 16, "HYPERLINK(\"http://www.andykhan.com/jexcelapi\", \"JExcelApi Home Page\")"); + ws.addCell(l); + + f = new Formula(2, 17, "3*4+5"); + ws.addCell(f); + l = new Label(3, 17, "3*4+5"); + ws.addCell(l); + + f = new Formula(2, 18, "\"Plain text formula\""); + ws.addCell(f); + l = new Label(3, 18, "Plain text formula"); + ws.addCell(l); + + f = new Formula(2, 19, "SUM(a1,a2,-a3,a4)"); + ws.addCell(f); + l = new Label(3, 19, "SUM(a1,a2,-a3,a4)"); + ws.addCell(l); + + f = new Formula(2, 20, "2*-(a1+a2)"); + ws.addCell(f); + l = new Label(3, 20, "2*-(a1+a2)"); + ws.addCell(l); + + f = new Formula(2, 21, "'Number Formats'!B1/2"); + ws.addCell(f); + l = new Label(3, 21, "'Number Formats'!B1/2"); + ws.addCell(l); + + f = new Formula(2, 22, "IF(F22=0, 0, F21/F22)"); + ws.addCell(f); + l = new Label(3, 22, "IF(F22=0, 0, F21/F22)"); + ws.addCell(l); + + f = new Formula(2, 23, "RAND()"); + ws.addCell(f); + l = new Label(3, 23, "RAND()"); + ws.addCell(l); + + StringBuffer buf = new StringBuffer(); + buf.append("'"); + buf.append(workbook.getSheet(0).getName()); + buf.append("'!"); + buf.append(CellReferenceHelper.getCellReference(9, 18)); + buf.append("*25"); + f = new Formula(2, 24, buf.toString()); + ws.addCell(f); + l = new Label(3, 24, buf.toString()); + ws.addCell(l); + + wcf = new WritableCellFormat(DateFormats.DEFAULT); + f = new Formula(2, 25, "NOW()", wcf); + ws.addCell(f); + l = new Label(3, 25, "NOW()"); + ws.addCell(l); + + f = new Formula(2, 26, "$A$2+A3"); + ws.addCell(f); + l = new Label(3, 26, "$A$2+A3"); + ws.addCell(l); + + f = new Formula(2, 27, "IF(COUNT(A1:A9,B1:B9)=0,\"\",COUNT(A1:A9,B1:B9))"); + ws.addCell(f); + l = new Label(3, 27, "IF(COUNT(A1:A9,B1:B9)=0,\"\",COUNT(A1:A9,B1:B9))"); + ws.addCell(l); + + f = new Formula(2, 28, "SUM(A1,A2,A3,A4)"); + ws.addCell(f); + l = new Label(3, 28, "SUM(A1,A2,A3,A4)"); + ws.addCell(l); + + l = new Label(1, 29, "a1"); + ws.addCell(l); + f = new Formula(2, 29, "SUM(INDIRECT(ADDRESS(2,29)):A4)"); + ws.addCell(f); + l = new Label(3, 29, "SUM(INDIRECT(ADDRESS(2,29):A4)"); + ws.addCell(l); + + f = new Formula(2, 30, "COUNTIF(A1:A4, \">=12\")"); + ws.addCell(f); + l = new Label(3, 30, "COUNTIF(A1:A4, \">=12\")"); + ws.addCell(l); + + f = new Formula(2, 31, "MAX($A$1:$A$4)"); + ws.addCell(f); + l = new Label(3, 31, "MAX($A$1:$A$4)"); + ws.addCell(l); + + f = new Formula(2, 32, "OR(A1,TRUE)"); + ws.addCell(f); + l = new Label(3, 32, "OR(A1,TRUE)"); + ws.addCell(l); + + f = new Formula(2, 33, "ROWS(A1:C14)"); + ws.addCell(f); + l = new Label(3, 33, "ROWS(A1:C14)"); + ws.addCell(l); + + f = new Formula(2, 34, "COUNTBLANK(A1:C14)"); + ws.addCell(f); + l = new Label(3, 34, "COUNTBLANK(A1:C14)"); + ws.addCell(l); + + f = new Formula(2, 35, "IF(((F1=\"Not Found\")*(F2=\"Not Found\")*(F3=\"\")*(F4=\"\")*(F5=\"\")),1,0)"); + ws.addCell(f); + l = new Label(3, 35, "IF(((F1=\"Not Found\")*(F2=\"Not Found\")*(F3=\"\")*(F4=\"\")*(F5=\"\")),1,0)"); + ws.addCell(l); + + f = new Formula(2, 36, + "HYPERLINK(\"http://www.amazon.co.uk/exec/obidos/ASIN/0571058086qid=1099836249/sr=1-3/ref=sr_1_11_3/202-6017285-1620664\", \"Long hyperlink\")"); + ws.addCell(f); + + f = new Formula(2, 37, "1234567+2699"); + ws.addCell(f); + l = new Label(3, 37, "1234567+2699"); + ws.addCell(l); + + f = new Formula(2, 38, "IF(ISERROR(G25/G29),0,-1)"); + ws.addCell(f); + l = new Label(3, 38, "IF(ISERROR(G25/G29),0,-1)"); + ws.addCell(l); + + f = new Formula(2, 39, "SEARCH(\"C\",D40)"); + ws.addCell(f); + l = new Label(3, 39, "SEARCH(\"C\",D40)"); + ws.addCell(l); + + f = new Formula(2, 40, "#REF!"); + ws.addCell(f); + l = new Label(3, 40, "#REF!"); + ws.addCell(l); + + nc = new Number (1,41, 79); + ws.addCell(nc); + f = new Formula(2, 41, "--B42"); + ws.addCell(f); + l = new Label(3, 41, "--B42"); + ws.addCell(l); + + f = new Formula(2, 42, "CHOOSE(3,A1,A2,A3,A4"); + ws.addCell(f); + l = new Label(3,42, "CHOOSE(3,A1,A2,A3,A4"); + ws.addCell(l); + + f = new Formula(2, 43, "A4-A3-A2"); + ws.addCell(f); + l = new Label(3,43, "A4-A3-A2"); + ws.addCell(l); + + f = new Formula(2, 44, "F29+F34+F41+F48+F55+F62+F69+F76+F83+F90+F97+F104+F111+F118+F125+F132+F139+F146+F153+F160+F167+F174+F181+F188+F195+F202+F209+F216+F223+F230+F237+F244+F251+F258+F265+F272+F279+F286+F293+F300+F305+F308"); + ws.addCell(f); + l = new Label(3, 44, "F29+F34+F41+F48+F55+F62+F69+F76+F83+F90+F97+F104+F111+F118+F125+F132+F139+F146+F153+F160+F167+F174+F181+F188+F195+F202+F209+F216+F223+F230+F237+F244+F251+F258+F265+F272+F279+F286+F293+F300+F305+F308"); + ws.addCell(l); + // Errors + /* + f = new Formula(2, 25, "PLOP(15)"); // unknown function + ws.addCell(f); + + f = new Formula(2, 26, "SUM(15,3"); // unmatched parentheses + ws.addCell(f); + + f = new Formula(2, 27, "SUM15,3)"); // missing opening parentheses + ws.addCell(f); + + f = new Formula(2, 28, "ROUND(3.14159)"); // missing args + ws.addCell(f); + + f = new Formula(2, 29, "NONSHEET!A1"); // sheet not found + ws.addCell(f); + */ + } + + /** + * Write out the images + */ + private void writeImageSheet(WritableSheet ws) throws WriteException + { + Label l = new Label(0, 0, "Weald & Downland Open Air Museum, Sussex"); + ws.addCell(l); + + WritableImage wi = new WritableImage + (0, 3, 5, 7, new File("resources/wealdanddownland.png")); + ws.addImage(wi); + + l = new Label(0, 12, "Merchant Adventurers Hall, York"); + ws.addCell(l); + + wi = new WritableImage(5, 12, 4, 10, + new File("resources/merchantadventurers.png")); + ws.addImage(wi); + + // An unsupported file type + /* + wi = new WritableImage(0, 60, 5, 5, new File("resources/somefile.gif")); + ws.addImage(wi); + */ + } +} + + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/demo/WriteAccess.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/demo/WriteAccess.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/demo/WriteAccess.java 17 Aug 2012 14:51:17 -0000 1.1 @@ -0,0 +1,82 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.demo; + +import java.io.FileInputStream; +import java.io.IOException; + +import jxl.WorkbookSettings; +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.read.biff.BiffException; +import jxl.read.biff.BiffRecordReader; +import jxl.read.biff.File; +import jxl.read.biff.Record; + +/** + * Displays whatever generated the excel file (ie. the WriteAccess record) + */ +class WriteAccess +{ + private BiffRecordReader reader; + + public WriteAccess(java.io.File file) + throws IOException, BiffException + { + WorkbookSettings ws = new WorkbookSettings(); + FileInputStream fis = new FileInputStream(file); + File f = new File(fis, ws); + reader = new BiffRecordReader(f); + + display(ws); + fis.close(); + } + + /** + * Dumps out the contents of the excel file + */ + private void display(WorkbookSettings ws) throws IOException + { + Record r = null; + boolean found = false; + while (reader.hasNext() && !found) + { + r = reader.next(); + if (r.getType() == Type.WRITEACCESS) + { + found = true; + } + } + + if (!found) + { + System.err.println("Warning: could not find write access record"); + return; + } + + byte[] data = r.getData(); + + String s = null; + + s = StringHelper.getString(data, data.length, 0, ws); + + System.out.println(s); + } +} Index: 3rdParty_sources/jexcelapi/jxl/demo/XML.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/demo/XML.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/demo/XML.java 17 Aug 2012 14:51:17 -0000 1.1 @@ -0,0 +1,326 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.demo; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; + +import jxl.Cell; +import jxl.CellType; +import jxl.Sheet; +import jxl.Workbook; +import jxl.format.Border; +import jxl.format.BorderLineStyle; +import jxl.format.CellFormat; +import jxl.format.Colour; +import jxl.format.Font; +import jxl.format.Pattern; + +/** + * Simple demo class which uses the api to present the contents + * of an excel 97 spreadsheet as an XML document, using a workbook + * and output stream of your choice + */ +public class XML +{ + /** + * The output stream to write to + */ + private OutputStream out; + + /** + * The encoding to write + */ + private String encoding; + + /** + * The workbook we are reading from + */ + private Workbook workbook; + + /** + * Constructor + * + * @param w The workbook to interrogate + * @param out The output stream to which the XML values are written + * @param enc The encoding used by the output stream. Null or + * unrecognized values cause the encoding to default to UTF8 + * @param f Indicates whether the generated XML document should contain + * the cell format information + * @exception java.io.IOException + */ + public XML(Workbook w, OutputStream out, String enc, boolean f) + throws IOException + { + encoding = enc; + workbook = w; + this.out = out; + + if (encoding == null || !encoding.equals("UnicodeBig")) + { + encoding = "UTF8"; + } + + if (f) + { + writeFormattedXML(); + } + else + { + writeXML(); + } + + } + + /** + * Writes out the workbook data as XML, without formatting information + */ + private void writeXML() throws IOException + { + try + { + OutputStreamWriter osw = new OutputStreamWriter(out, encoding); + BufferedWriter bw = new BufferedWriter(osw); + + bw.write(""); + bw.newLine(); + bw.write(""); + bw.newLine(); + bw.newLine(); + bw.write(""); + bw.newLine(); + for (int sheet = 0; sheet < workbook.getNumberOfSheets(); sheet++) + { + Sheet s = workbook.getSheet(sheet); + + bw.write(" "); + bw.newLine(); + bw.write(" "); + bw.newLine(); + + Cell[] row = null; + + for (int i = 0 ; i < s.getRows() ; i++) + { + bw.write(" "); + bw.newLine(); + row = s.getRow(i); + + for (int j = 0 ; j < row.length; j++) + { + if (row[j].getType() != CellType.EMPTY) + { + bw.write(" "); + bw.write(""); + bw.write(""); + bw.newLine(); + } + } + bw.write(" "); + bw.newLine(); + } + bw.write(" "); + bw.newLine(); + } + + bw.write(""); + bw.newLine(); + + bw.flush(); + bw.close(); + } + catch (UnsupportedEncodingException e) + { + System.err.println(e.toString()); + } + } + + /** + * Writes out the workbook data as XML, with formatting information + */ + private void writeFormattedXML() throws IOException + { + try + { + OutputStreamWriter osw = new OutputStreamWriter(out, encoding); + BufferedWriter bw = new BufferedWriter(osw); + + bw.write(""); + bw.newLine(); + bw.write(""); + bw.newLine(); + bw.newLine(); + bw.write(""); + bw.newLine(); + for (int sheet = 0; sheet < workbook.getNumberOfSheets(); sheet++) + { + Sheet s = workbook.getSheet(sheet); + + bw.write(" "); + bw.newLine(); + bw.write(" "); + bw.newLine(); + + Cell[] row = null; + CellFormat format = null; + Font font = null; + + for (int i = 0 ; i < s.getRows() ; i++) + { + bw.write(" "); + bw.newLine(); + row = s.getRow(i); + + for (int j = 0 ; j < row.length; j++) + { + // Remember that empty cells can contain format information + if ((row[j].getType() != CellType.EMPTY) || + (row[j].getCellFormat() != null)) + { + format = row[j].getCellFormat(); + bw.write(" "); + bw.newLine(); + bw.write(" "); + bw.write(""); + bw.write(""); + bw.newLine(); + + if (row[j].getCellFormat() != null) + { + bw.write(" "); + bw.newLine(); + + // The font information + font = format.getFont(); + bw.write(" "); + bw.newLine(); + + + // The cell background information + if (format.getBackgroundColour() != Colour.DEFAULT_BACKGROUND || + format.getPattern() != Pattern.NONE) + { + bw.write(" "); + bw.newLine(); + } + + + // The cell border, if it has one + if (format.getBorder(Border.TOP ) != BorderLineStyle.NONE || + format.getBorder(Border.BOTTOM) != BorderLineStyle.NONE || + format.getBorder(Border.LEFT) != BorderLineStyle.NONE || + format.getBorder(Border.RIGHT) != BorderLineStyle.NONE) + { + + bw.write(" "); + bw.newLine(); + } + + // The cell number/date format + if (!format.getFormat().getFormatString().equals("")) + { + bw.write(" "); + bw.newLine(); + } + + bw.write(" "); + bw.newLine(); + } + + bw.write(" "); + bw.newLine(); + } + } + bw.write(" "); + bw.newLine(); + } + bw.write(" "); + bw.newLine(); + } + + bw.write(""); + bw.newLine(); + + bw.flush(); + bw.close(); + } + catch (UnsupportedEncodingException e) + { + System.err.println(e.toString()); + } + } + +} + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/format/Alignment.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/format/Alignment.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/format/Alignment.java 17 Aug 2012 14:50:58 -0000 1.1 @@ -0,0 +1,126 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.format; + +/** + * Enumeration class which contains the various alignments for data within a + * cell + */ +public /*final*/ class Alignment +{ + /** + * The internal numerical repreentation of the alignment + */ + private int value; + + /** + * The string description of this alignment + */ + private String string; + + /** + * The list of alignments + */ + private static Alignment[] alignments = new Alignment[0]; + + /** + * Private constructor + * + * @param val + * @param string + */ + protected Alignment(int val,String s) + { + value = val; + string = s; + + Alignment[] oldaligns = alignments; + alignments = new Alignment[oldaligns.length + 1]; + System.arraycopy(oldaligns, 0, alignments, 0, oldaligns.length); + alignments[oldaligns.length] = this; + } + + /** + * Gets the value of this alignment. This is the value that is written to + * the generated Excel file + * + * @return the binary value + */ + public int getValue() + { + return value; + } + + /** + * Gets the string description of this alignment + * + * @return the string description + */ + public String getDescription() + { + return string; + } + + /** + * Gets the alignment from the value + * + * @param val + * @return the alignment with that value + */ + public static Alignment getAlignment(int val) + { + for (int i = 0 ; i < alignments.length ; i++) + { + if (alignments[i].getValue() == val) + { + return alignments[i]; + } + } + + return GENERAL; + } + + /** + * The standard alignment + */ + public static Alignment GENERAL = new Alignment(0, "general"); + /** + * Data cells with this alignment will appear at the left hand edge of the + * cell + */ + public static Alignment LEFT = new Alignment(1, "left"); + /** + * Data in cells with this alignment will be centred + */ + public static Alignment CENTRE = new Alignment(2, "centre"); + /** + * Data in cells with this alignment will be right aligned + */ + public static Alignment RIGHT = new Alignment(3, "right"); + /** + * Data in cells with this alignment will fill the cell + */ + public static Alignment FILL = new Alignment(4,"fill"); + /** + * Data in cells with this alignment will be justified + */ + public static Alignment JUSTIFY = new Alignment(5, "justify"); +} + Index: 3rdParty_sources/jexcelapi/jxl/format/BoldStyle.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/format/BoldStyle.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/format/BoldStyle.java 17 Aug 2012 14:50:58 -0000 1.1 @@ -0,0 +1,81 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.format; + +/** + * Enumeration class containing the various bold styles for data + */ +public /*final*/ class BoldStyle +{ + /** + * The bold weight + */ + private int value; + + /** + * The description + */ + private String string; + + /** + * Constructor + * + * @param val + */ + protected BoldStyle(int val, String s) + { + value = val; + string = s; + } + + /** + * Gets the value of the bold weight. This is the value that will be + * written to the generated Excel file. + * + * @return the bold weight + */ + public int getValue() + { + return value ; + } + + /** + * Gets the string description of the bold style + */ + public String getDescription() + { + return string; + } + + /** + * Normal style + */ + public static final BoldStyle NORMAL = new BoldStyle(0x190, "Normal"); + /** + * Emboldened style + */ + public static final BoldStyle BOLD = new BoldStyle(0x2bc, "Bold"); +} + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/format/Border.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/format/Border.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/format/Border.java 17 Aug 2012 14:50:58 -0000 1.1 @@ -0,0 +1,55 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.format; + +/** + * The location of a border + */ +public /*final*/ class Border +{ + /** + * The string description + */ + private String string; + + /** + * Constructor + */ + protected Border(String s) + { + string = s; + } + + /** + * Gets the description + */ + public String getDescription() + { + return string; + } + + public final static Border NONE = new Border("none"); + public final static Border ALL = new Border("all"); + public final static Border TOP = new Border("top"); + public final static Border BOTTOM = new Border("bottom"); + public final static Border LEFT = new Border("left"); + public final static Border RIGHT = new Border("right"); +} + Index: 3rdParty_sources/jexcelapi/jxl/format/BorderLineStyle.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/format/BorderLineStyle.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/format/BorderLineStyle.java 17 Aug 2012 14:50:58 -0000 1.1 @@ -0,0 +1,122 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.format; + +/** + * The border line style + */ +public /*final*/ class BorderLineStyle +{ + /** + * The value + */ + private int value; + + /** + * The string description + */ + private String string; + + /** + * The list of alignments + */ + private static BorderLineStyle[] styles = new BorderLineStyle[0]; + + + /** + * Constructor + */ + protected BorderLineStyle(int val, String s) + { + value = val; + string = s; + + BorderLineStyle[] oldstyles = styles; + styles = new BorderLineStyle[oldstyles.length + 1]; + System.arraycopy(oldstyles, 0, styles, 0, oldstyles.length); + styles[oldstyles.length] = this; + } + + /** + * Gets the value for this line style + * + * @return the value + */ + public int getValue() + { + return value; + } + + /** + * Gets the textual description + */ + public String getDescription() + { + return string; + } + + /** + * Gets the alignment from the value + * + * @param val + * @return the alignment with that value + */ + public static BorderLineStyle getStyle(int val) + { + for (int i = 0 ; i < styles.length ; i++) + { + if (styles[i].getValue() == val) + { + return styles[i]; + } + } + + return NONE; + } + + public final static BorderLineStyle NONE + = new BorderLineStyle(0, "none"); + public final static BorderLineStyle THIN + = new BorderLineStyle(1, "thin"); + public final static BorderLineStyle MEDIUM + = new BorderLineStyle(2, "medium"); + public final static BorderLineStyle DASHED + = new BorderLineStyle(3, "dashed"); + public final static BorderLineStyle DOTTED + = new BorderLineStyle(4, "dotted"); + public final static BorderLineStyle THICK + = new BorderLineStyle(5, "thick"); + public final static BorderLineStyle DOUBLE + = new BorderLineStyle(6, "double"); + public final static BorderLineStyle HAIR + = new BorderLineStyle(7, "hair"); + public final static BorderLineStyle MEDIUM_DASHED + = new BorderLineStyle(8, "medium dashed"); + public final static BorderLineStyle DASH_DOT + = new BorderLineStyle(9, "dash dot"); + public final static BorderLineStyle MEDIUM_DASH_DOT + = new BorderLineStyle(0xa, "medium dash dot"); + public final static BorderLineStyle DASH_DOT_DOT + = new BorderLineStyle(0xb, "Dash dot dot"); + public final static BorderLineStyle MEDIUM_DASH_DOT_DOT + = new BorderLineStyle(0xc, "Medium dash dot dot"); + public final static BorderLineStyle SLANTED_DASH_DOT + = new BorderLineStyle(0xd, "Slanted dash dot"); +} Index: 3rdParty_sources/jexcelapi/jxl/format/CellFormat.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/format/CellFormat.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/format/CellFormat.java 17 Aug 2012 14:50:58 -0000 1.1 @@ -0,0 +1,143 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.format; + +/** + * Interface for cell formats + */ +public interface CellFormat +{ + /** + * Gets the format used by this format + * + * @return the format + */ + public Format getFormat(); + + /** + * Gets the font information used by this format + * + * @return the font + */ + public Font getFont(); + + /** + * Gets whether or not the contents of this cell are wrapped + * + * @return TRUE if this cell's contents are wrapped, FALSE otherwise + */ + public boolean getWrap(); + + /** + * Gets the horizontal cell alignment + * + * @return the alignment + */ + public Alignment getAlignment(); + + /** + * Gets the vertical cell alignment + * + * @return the alignment + */ + public VerticalAlignment getVerticalAlignment(); + + /** + * Gets the orientation + * + * @return the orientation + */ + public Orientation getOrientation(); + + /** + * Gets the line style for the given cell border + * If a border type of ALL or NONE is specified, then a line style of + * NONE is returned + * + * @param border the cell border we are interested in + * @return the line style of the specified border + */ + public BorderLineStyle getBorder(Border border); + + /** + * Gets the line style for the given cell border + * If a border type of ALL or NONE is specified, then a line style of + * NONE is returned + * + * @param border the cell border we are interested in + * @return the line style of the specified border + */ + public BorderLineStyle getBorderLine(Border border); + + /** + * Gets the colour for the given cell border + * If a border type of ALL or NONE is specified, then a line style of + * NONE is returned + * If the specified cell does not have an associated line style, then + * the colour the line would be is still returned + * + * @param border the cell border we are interested in + * @return the line style of the specified border + */ + public Colour getBorderColour(Border border); + + /** + * Determines if this cell format has any borders at all. Used to + * set the new borders when merging a group of cells + * + * @return TRUE if this cell has any borders, FALSE otherwise + */ + public boolean hasBorders(); + + /** + * Gets the background colour used by this cell + * + * @return the foreground colour + */ + public Colour getBackgroundColour(); + + /** + * Gets the pattern used by this cell format + * + * @return the background pattern + */ + public Pattern getPattern(); + + /** + * Gets the indentation of the cell text + * + * @return the indentation + */ + public int getIndentation(); + + /** + * Gets the shrink to fit flag + * + * @return TRUE if this format is shrink to fit, FALSE otherise + */ + public boolean isShrinkToFit(); + + /** + * Accessor for whether a particular cell is locked + * + * @return TRUE if this cell is locked, FALSE otherwise + */ + public boolean isLocked(); +} Index: 3rdParty_sources/jexcelapi/jxl/format/Colour.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/format/Colour.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/format/Colour.java 17 Aug 2012 14:50:58 -0000 1.1 @@ -0,0 +1,256 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.format; + +/** + * Enumeration class which contains the various colours available within + * the standard Excel colour palette + * + */ +public /*final*/ class Colour +{ + /** + * The internal numerical representation of the colour + */ + private int value; + + /** + * The default RGB value + */ + private RGB rgb; + + /** + * The display string for the colour. Used when presenting the + * format information + */ + private String string; + + /** + * The list of internal colours + */ + private static Colour[] colours = new Colour[0]; + + /** + * Private constructor + * + * @param val + * @param s the display string + * @param r the default red value + * @param g the default green value + * @param b the default blue value + */ + protected Colour(int val, String s, int r, int g, int b) + { + value = val; + string = s; + rgb = new RGB(r,g,b); + + Colour[] oldcols = colours; + colours = new Colour[oldcols.length + 1]; + System.arraycopy(oldcols, 0, colours, 0, oldcols.length); + colours[oldcols.length] = this; + } + + /** + * Gets the value of this colour. This is the value that is written to + * the generated Excel file + * + * @return the binary value + */ + public int getValue() + { + return value; + } + + /** + * Gets the string description for display purposes + * + * @return the string description + */ + public String getDescription() + { + return string; + } + + /** + * Gets the default red content of this colour. Used when writing the + * default colour palette + * + * @return the red content of this colour + * @deprecated use getDefaultRGB instead + */ + public int getDefaultRed() + { + return rgb.getRed(); + } + + /** + * Gets the default green content of this colour. Used when writing the + * default colour palette + * + * @return the green content of this colour + * @deprecated use getDefaultRGB instead + */ + public int getDefaultGreen() + { + return rgb.getGreen(); + } + + /** + * Gets the default blue content of this colour. Used when writing the + * default colour palette + * + * @return the blue content of this colour + * @deprecated use getDefaultRGB instead + */ + public int getDefaultBlue() + { + return rgb.getBlue(); + } + + /** + * Returns the default RGB of the colour + * + * @return the default RGB + */ + public RGB getDefaultRGB() + { + return rgb; + } + + /** + * Gets the internal colour from the value + * + * @param val + * @return the colour with that value + */ + public static Colour getInternalColour(int val) + { + for (int i = 0 ; i < colours.length ; i++) + { + if (colours[i].getValue() == val) + { + return colours[i]; + } + } + + return UNKNOWN; + } + + /** + * Returns all available colours - used when generating the default palette + * + * @return all available colours + */ + public static Colour[] getAllColours() + { + return colours; + } + + // Major colours + public final static Colour UNKNOWN = + new Colour(0x7fee, "unknown", 0, 0, 0); + public final static Colour BLACK = + new Colour(0x7fff, "black", 0, 0, 0); + public final static Colour WHITE = + new Colour(0x9, "white", 0xff, 0xff, 0xff); + public final static Colour DEFAULT_BACKGROUND1 + = new Colour(0x0, "default background", 0xff, 0xff, 0xff); + public final static Colour DEFAULT_BACKGROUND + = new Colour(0xc0, "default background", 0xff, 0xff, 0xff); + public final static Colour PALETTE_BLACK = + new Colour(0x8, "black", 0x1, 0, 0); + // the first item in the colour palette + + // Other colours + public final static Colour RED = new Colour(0xa, "red",0xff,0,0); + public final static Colour BRIGHT_GREEN = new Colour(0xb, "bright green",0,0xff,0); + public final static Colour BLUE = new Colour(0xc, "blue",0,0,0xff); public final static Colour YELLOW = new Colour(0xd, "yellow",0xff,0xff,0); + public final static Colour PINK = new Colour(0xe, "pink",0xff,0,0xff); + public final static Colour TURQUOISE = new Colour(0xf, "turquoise",0,0xff,0xff); + public final static Colour DARK_RED = new Colour(0x10, "dark red",0x80,0,0); + public final static Colour GREEN = new Colour(0x11, "green",0,0x80,0); + public final static Colour DARK_BLUE = new Colour(0x12, "dark blue",0,0,0x80); + public final static Colour DARK_YELLOW = new Colour(0x13, "dark yellow",0x80,0x80,0); + public final static Colour VIOLET = new Colour(0x14, "violet",0x80,0x80,0); + public final static Colour TEAL = new Colour(0x15, "teal",0,0x80,0x80); + public final static Colour GREY_25_PERCENT = new Colour(0x16 ,"grey 25%",0xc0,0xc0,0xc0); + public final static Colour GREY_50_PERCENT = new Colour(0x17, "grey 50%",0x80,0x80,0x80); + public final static Colour PERIWINKLE = new Colour(0x18, "periwinkle%",0x99, 0x99, 0xff); + public final static Colour PLUM2 = new Colour(0x19, "plum",0x99, 0x33, 0x66); + public final static Colour IVORY = new Colour(0x1a, "ivory",0xff, 0xff, 0xcc); + public final static Colour LIGHT_TURQUOISE2= new Colour(0x1b, "light turquoise",0xcc, 0xff, 0xff); + public final static Colour DARK_PURPLE = new Colour(0x1c, "dark purple",0x66, 0x0, 0x66); + public final static Colour CORAL = new Colour(0x1d, "coral",0xff, 0x80, 0x80); + public final static Colour OCEAN_BLUE = new Colour(0x1e, "ocean blue",0x0, 0x66, 0xcc); + public final static Colour ICE_BLUE = new Colour(0x1f, "ice blue",0xcc, 0xcc, 0xff); + public final static Colour DARK_BLUE2 = new Colour(0x20, "dark blue",0,0,0x80); + public final static Colour PINK2 = new Colour(0x21, "pink",0xff,0,0xff); + public final static Colour YELLOW2 = new Colour(0x22, "yellow",0xff,0xff,0x0); + public final static Colour TURQOISE2 = new Colour(0x23, "turqoise",0x0,0xff,0xff); + public final static Colour VIOLET2 = new Colour(0x24, "violet",0x80,0x0,0x80); + public final static Colour DARK_RED2 = new Colour(0x25, "dark red",0x80,0x0,0x0); + public final static Colour TEAL2 = new Colour(0x26, "teal",0x0,0x80,0x80); + public final static Colour BLUE2 = new Colour(0x27, "blue",0x0,0x0,0xff); + public final static Colour SKY_BLUE = new Colour(0x28, "sky blue",0,0xcc,0xff); + public final static Colour LIGHT_TURQUOISE + = new Colour(0x29, "light turquoise",0xcc,0xff,0xff); + public final static Colour LIGHT_GREEN = new Colour(0x2a, "light green",0xcc,0xff,0xcc); + public final static Colour VERY_LIGHT_YELLOW + = new Colour(0x2b, "very light yellow",0xff,0xff,0x99); + public final static Colour PALE_BLUE = new Colour(0x2c, "pale blue",0x99,0xcc,0xff); + public final static Colour ROSE = new Colour(0x2d, "rose",0xff,0x99,0xcc); + public final static Colour LAVENDER = new Colour(0x2e, "lavender",0xcc,0x99,0xff); + public final static Colour TAN = new Colour(0x2f, "tan",0xff,0xcc,0x99); + public final static Colour LIGHT_BLUE = new Colour(0x30, "light blue", 0x33, 0x66, 0xff); + public final static Colour AQUA = new Colour(0x31, "aqua",0x33,0xcc,0xcc); + public final static Colour LIME = new Colour(0x32, "lime",0x99,0xcc,0); + public final static Colour GOLD = new Colour(0x33, "gold",0xff,0xcc,0); + public final static Colour LIGHT_ORANGE + = new Colour(0x34, "light orange",0xff,0x99,0); + public final static Colour ORANGE = new Colour(0x35, "orange",0xff,0x66,0); + public final static Colour BLUE_GREY = new Colour(0x36, "blue grey",0x66,0x66,0xcc); + public final static Colour GREY_40_PERCENT = new Colour(0x37, "grey 40%",0x96,0x96,0x96); + public final static Colour DARK_TEAL = new Colour(0x38, "dark teal",0,0x33,0x66); + public final static Colour SEA_GREEN = new Colour(0x39, "sea green",0x33,0x99,0x66); + public final static Colour DARK_GREEN = new Colour(0x3a, "dark green",0,0x33,0); + public final static Colour OLIVE_GREEN = new Colour(0x3b, "olive green",0x33,0x33,0); + public final static Colour BROWN = new Colour(0x3c, "brown",0x99,0x33,0); + public final static Colour PLUM = new Colour(0x3d, "plum",0x99,0x33,0x66); + public final static Colour INDIGO = new Colour(0x3e, "indigo",0x33,0x33,0x99); + public final static Colour GREY_80_PERCENT = new Colour(0x3f, "grey 80%",0x33,0x33,0x33); + public final static Colour AUTOMATIC = new Colour(0x40, "automatic", 0xff, 0xff, 0xff); + + // Colours added for backwards compatibility + public final static Colour GRAY_80 = GREY_80_PERCENT; + public final static Colour GRAY_50 = GREY_50_PERCENT; + public final static Colour GRAY_25 = GREY_25_PERCENT; +} + + + + + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/format/Font.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/format/Font.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/format/Font.java 17 Aug 2012 14:50:58 -0000 1.1 @@ -0,0 +1,83 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.format; + +/** + * Interface which exposes the user font display information to the user + */ +public interface Font +{ + /** + * Gets the name of this font + * + * @return the name of this font + */ + public String getName(); + + /** + * Gets the point size for this font, if the font hasn't been initialized + * + * @return the point size + */ + public int getPointSize(); + + /** + * Gets the bold weight for this font + * + * @return the bold weight for this font + */ + public int getBoldWeight(); + + /** + * Returns the italic flag + * + * @return TRUE if this font is italic, FALSE otherwise + */ + public boolean isItalic(); + + /** + * Returns the strike-out flag + * + * @return TRUE if this font is struck-out, FALSE otherwise + */ + public boolean isStruckout(); + + /** + * Gets the underline style for this font + * + * @return the underline style + */ + public UnderlineStyle getUnderlineStyle(); + + /** + * Gets the colour for this font + * + * @return the colour + */ + public Colour getColour(); + + /** + * Gets the script style + * + * @return the script style + */ + public ScriptStyle getScriptStyle(); +} + Index: 3rdParty_sources/jexcelapi/jxl/format/Format.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/format/Format.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/format/Format.java 17 Aug 2012 14:50:58 -0000 1.1 @@ -0,0 +1,41 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.format; + +/** + * Exposes the cell formatting information + */ +public interface Format +{ + /** + * Accesses the excel format string which is applied to the cell + * Note that this is the string that excel uses, and not the java + * equivalent + * + * @return the cell format string + */ + public String getFormatString(); +} + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/format/Orientation.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/format/Orientation.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/format/Orientation.java 17 Aug 2012 14:50:58 -0000 1.1 @@ -0,0 +1,139 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.format; + +/** + * Enumeration type which describes the orientation of data within a cell + */ +public final class Orientation +{ + /** + * The internal binary value which gets written to the generated Excel file + */ + private int value; + + /** + * The textual description + */ + private String string; + + /** + * The list of alignments + */ + private static Orientation[] orientations = new Orientation[0]; + + /** + * Constructor + * + * @param val + */ + protected Orientation(int val, String s) + { + value = val; string = s; + + Orientation[] oldorients = orientations; + orientations = new Orientation[oldorients.length + 1]; + System.arraycopy(oldorients, 0, orientations, 0, oldorients.length); + orientations[oldorients.length] = this; + } + + /** + * Accessor for the binary value + * + * @return the internal binary value + */ + public int getValue() + { + return value; + } + + /** + * Gets the textual description + */ + public String getDescription() + { + return string; + } + + /** + * Gets the alignment from the value + * + * @param val + * @return the alignment with that value + */ + public static Orientation getOrientation(int val) + { + for (int i = 0 ; i < orientations.length ; i++) + { + if (orientations[i].getValue() == val) + { + return orientations[i]; + } + } + + return HORIZONTAL; + } + + + /** + * Cells with this specified orientation will be horizontal + */ + public static Orientation HORIZONTAL = new Orientation(0, "horizontal"); + /** + * Cells with this specified orientation have their data + * presented vertically + */ + public static Orientation VERTICAL = new Orientation(0xff, "vertical"); + /** + * Cells with this specified orientation will have their data + * presented with a rotation of 90 degrees upwards + */ + public static Orientation PLUS_90 = new Orientation(90, "up 90"); + /** + * Cells with this specified orientation will have their data + * presented with a rotation of 90 degrees downwards + */ + public static Orientation MINUS_90 = new Orientation(180, "down 90"); + /** + * Cells with this specified orientation will have their data + * presented with a rotation 45 degrees upwards + */ + public static Orientation PLUS_45 = new Orientation(45, "up 45"); + /** + * Cells with this specified orientation will have their data + * presented with a rotation 45 degrees downwards + */ + public static Orientation MINUS_45 = new Orientation(135, "down 45"); + /** + * Cells with this specified orientation will have their text stacked + * downwards, but not rotated + */ + public static Orientation STACKED = new Orientation(255, "stacked"); + +} + + + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/format/PageOrder.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/format/PageOrder.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/format/PageOrder.java 17 Aug 2012 14:50:58 -0000 1.1 @@ -0,0 +1,43 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.format; + +/** + * Enumeration type which describes the page orientation + */ +public class PageOrder +{ + /** + * Constructor + */ + private PageOrder() + { + } + + /** + * Top to Down then Right. + */ + public static PageOrder DOWN_THEN_RIGHT = new PageOrder(); + + /** + * Left to Right then Down. + */ + public static PageOrder RIGHT_THEN_DOWN = new PageOrder(); +} Index: 3rdParty_sources/jexcelapi/jxl/format/PageOrientation.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/format/PageOrientation.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/format/PageOrientation.java 17 Aug 2012 14:50:58 -0000 1.1 @@ -0,0 +1,52 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.format; + +/** + * Enumeration type which describes the page orientation + */ +public final class PageOrientation +{ + /** + * Constructor + */ + private PageOrientation() + { + } + + + /** + * Portrait orientation + */ + public static PageOrientation PORTRAIT = new PageOrientation(); + /** + * Landscape orientation + */ + public static PageOrientation LANDSCAPE = new PageOrientation(); +} + + + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/format/PaperSize.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/format/PaperSize.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/format/PaperSize.java 17 Aug 2012 14:50:58 -0000 1.1 @@ -0,0 +1,334 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.format; + +/** + * Enumeration type which contains the available excel paper sizes and their + * codes + */ +public final class PaperSize +{ + private static final int LAST_PAPER_SIZE = 89; + /** + * The excel encoding + */ + private int val; + + /** + * The paper sizes + */ + private static PaperSize[] paperSizes = new PaperSize[LAST_PAPER_SIZE + 1]; + + /** + * Constructor + */ + private PaperSize(int v, boolean growArray) + { + val = v; + + if (v >= paperSizes.length && growArray) + { + // Grow the array and add this to it + PaperSize[] newarray = new PaperSize[v + 1]; + System.arraycopy(paperSizes, 0, newarray, 0, paperSizes.length); + paperSizes = newarray; + } + if (v < paperSizes.length) + { + paperSizes[v] = this; + } + } + + /** + * Constructor + */ + private PaperSize(int v) + { + this(v, true); + } + + /** + * Accessor for the internal binary value association with this paper size + * + * @return the internal value + */ + public int getValue() + { + return val; + } + + /** + * Gets the paper size for a specific value + * + * @param val the value + * @return the paper size + */ + public static PaperSize getPaperSize(int val) + { + PaperSize p = val > paperSizes.length - 1 ? null : paperSizes[val]; + return p == null ? new PaperSize(val, false) : p; + } + + /** US Letter 8.5 x 11" */ + public static final PaperSize UNDEFINED = new PaperSize(0); + + /** US Letter 8.5 x 11" */ + public static final PaperSize LETTER = new PaperSize(1); + + /** Letter small 8.5" � 11" */ + public static final PaperSize LETTER_SMALL = new PaperSize(2); + + /** Tabloid 11" x 17" */ + public static final PaperSize TABLOID = new PaperSize(3); + + /** Leger 17" x 11" */ + public static final PaperSize LEDGER = new PaperSize(4); + + /** US Legal 8.5" x 14" */ + public static final PaperSize LEGAL = new PaperSize(5); + + /** Statement 5.5" x 8.5" */ + public static final PaperSize STATEMENT = new PaperSize(6); + + /** Executive 7.25" x 10.5" */ + public static final PaperSize EXECUTIVE = new PaperSize(7); + + /** A3 297mm x 420mm */ + public static final PaperSize A3 = new PaperSize(8); + + /** A4 210mm x 297mm */ + public static final PaperSize A4 = new PaperSize(9); + + /** A4 Small 210mm x 297 mm */ + public static final PaperSize A4_SMALL = new PaperSize(10); + + /** A5 148mm x 210mm */ + public static final PaperSize A5 = new PaperSize(11); + + /** B4 (JIS) 257mm x 364mm */ + public static final PaperSize B4 = new PaperSize(12); + + /** B5 (JIS) 182mm x 257mm */ + public static final PaperSize B5 = new PaperSize(13); + + /** Folio 8.5" x 13" */ + public static final PaperSize FOLIO = new PaperSize(14); + + /** Quarto 215mm x 275mm */ + public static final PaperSize QUARTO = new PaperSize(15); + + /** 10" x 14" */ + public static final PaperSize SIZE_10x14 = new PaperSize(16); + + /** 11" x 17" */ + public static final PaperSize SIZE_10x17 = new PaperSize(17); + + /** NOTE 8.5" x 11" */ + public static final PaperSize NOTE = new PaperSize(18); + + /** Envelope #9 3 7/8" x 8 7/8" */ + public static final PaperSize ENVELOPE_9 = new PaperSize(19); + + /** Envelope #10 4 1/8" x 9.5" */ + public static final PaperSize ENVELOPE_10 = new PaperSize(20); + + /** Envelope #11 4.5" x 10 3/8" */ + public static final PaperSize ENVELOPE_11 = new PaperSize(21); + + /** Envelope #12 4.75" x 11" */ + public static final PaperSize ENVELOPE_12 = new PaperSize(22); + + /** Envelope #14 5" x 11.5" */ + public static final PaperSize ENVELOPE_14 = new PaperSize(23); + + /** C 17" x 22" */ + public static final PaperSize C = new PaperSize(24); + + /** D 22" x 34" */ + public static final PaperSize D = new PaperSize(25); + + /** E 34" x 44" */ + public static final PaperSize E = new PaperSize(26); + + /** Envelope DL 110mm � 220mm */ + public static final PaperSize ENVELOPE_DL = new PaperSize(27); + + /** Envelope C5 162mm � 229mm */ + public static final PaperSize ENVELOPE_C5 = new PaperSize(28); + + /** Envelope C3 324mm � 458mm */ + public static final PaperSize ENVELOPE_C3 = new PaperSize(29); + + /** Envelope C4 229mm � 324mm */ + public static final PaperSize ENVELOPE_C4 = new PaperSize(30); + + /** Envelope C6 114mm � 162mm */ + public static final PaperSize ENVELOPE_C6 = new PaperSize(31); + + /** Envelope C6/C5 114mm � 229mm */ + public static final PaperSize ENVELOPE_C6_C5 = new PaperSize(32); + + /** B4 (ISO) 250mm � 353mm */ + public static final PaperSize B4_ISO = new PaperSize(33); + + /** B5 (ISO) 176mm � 250mm */ + public static final PaperSize B5_ISO = new PaperSize(34); + + /** B6 (ISO) 125mm � 176mm */ + public static final PaperSize B6_ISO = new PaperSize(35); + + /** Envelope Italy 110mm � 230mm */ + public static final PaperSize ENVELOPE_ITALY = new PaperSize(36); + + /** Envelope Monarch 3 7/8" � 7.5" */ + public static final PaperSize ENVELOPE_MONARCH = new PaperSize(37); + + /** 6.75 Envelope 3 5/8" � 6.5" */ + public static final PaperSize ENVELOPE_6_75 = new PaperSize(38); + + /** US Standard Fanfold 14 7/8" � 11" */ + public static final PaperSize US_FANFOLD = new PaperSize(39); + + /** German Std. Fanfold 8.5" � 12" */ + public static final PaperSize GERMAN_FANFOLD = new PaperSize(40); + + /** German Legal Fanfold 8.5" � 13" */ + public static final PaperSize GERMAN_LEGAL_FANFOLD = new PaperSize(41); + + /** B4 (ISO) 250mm � 353mm */ + public static final PaperSize B4_ISO_2 = new PaperSize(42); + + /** Japanese Postcard 100mm � 148mm */ + public static final PaperSize JAPANESE_POSTCARD = new PaperSize(43); + + /** 9�11 9" � 11" */ + public static final PaperSize SIZE_9x11 = new PaperSize(44); + + /** 10�11 10" � 11" */ + public static final PaperSize SIZE_10x11 = new PaperSize(45); + + /** 15�11 15" � 11" */ + public static final PaperSize SIZE_15x11 = new PaperSize(46); + + /** Envelope Invite 220mm � 220mm */ + public static final PaperSize ENVELOPE_INVITE = new PaperSize(47); + + /* 48 & 49 Undefined */ + + /** Letter Extra 9.5" � 12" */ + public static final PaperSize LETTER_EXTRA = new PaperSize(50); + + /** Legal Extra 9.5" � 15" */ + public static final PaperSize LEGAL_EXTRA = new PaperSize(51); + + /** Tabloid Extra 11 11/16" � 18" */ + public static final PaperSize TABLOID_EXTRA = new PaperSize(52); + + /** A4 Extra 235mm � 322mm */ + public static final PaperSize A4_EXTRA = new PaperSize(53); + + /** Letter Transverse 8.5" � 11" */ + public static final PaperSize LETTER_TRANSVERSE = new PaperSize(54); + + /** A4 Transverse 210mm � 297mm */ + public static final PaperSize A4_TRANSVERSE = new PaperSize(55); + + /** Letter Extra Transv. 9.5" � 12" */ + public static final PaperSize LETTER_EXTRA_TRANSVERSE = new PaperSize(56); + + /** Super A/A4 227mm � 356mm */ + public static final PaperSize SUPER_A_A4 = new PaperSize(57); + + /** Super B/A3 305mm � 487mm */ + public static final PaperSize SUPER_B_A3 = new PaperSize(58); + + /** Letter Plus 8.5" x 12 11/16" */ + public static final PaperSize LETTER_PLUS = new PaperSize(59); + + /** A4 Plus 210mm � 330mm */ + public static final PaperSize A4_PLUS = new PaperSize(60); + + /** A5 Transverse 148mm � 210mm */ + public static final PaperSize A5_TRANSVERSE = new PaperSize(61); + + /** B5 (JIS) Transverse 182mm � 257mm */ + public static final PaperSize B5_TRANSVERSE = new PaperSize(62); + + /** A3 Extra 322mm � 445mm */ + public static final PaperSize A3_EXTRA = new PaperSize(63); + + /** A5 Extra 174mm � 235mm */ + public static final PaperSize A5_EXTRA = new PaperSize(64); + + /** B5 (ISO) Extra 201mm � 276mm */ + public static final PaperSize B5_EXTRA = new PaperSize(65); + + /** A2 420mm � 594mm */ + public static final PaperSize A2 = new PaperSize(66); + + /** A3 Transverse 297mm � 420mm */ + public static final PaperSize A3_TRANSVERSE = new PaperSize(67); + + /** A3 Extra Transverse 322mm � 445mm */ + public static final PaperSize A3_EXTRA_TRANSVERSE = new PaperSize(68); + + /** Dbl. Japanese Postcard 200mm � 148mm */ + public static final PaperSize DOUBLE_JAPANESE_POSTCARD = new PaperSize(69); + + /** A6 105mm � 148mm */ + public static final PaperSize A6 = new PaperSize(70); + + /* 71 - 74 undefined */ + + /** Letter Rotated 11" � 8.5" */ + public static final PaperSize LETTER_ROTATED = new PaperSize(75); + + /** A3 Rotated 420mm � 297mm */ + public static final PaperSize A3_ROTATED = new PaperSize(76); + + /** A4 Rotated 297mm � 210mm */ + public static final PaperSize A4_ROTATED = new PaperSize(77); + + /** A5 Rotated 210mm � 148mm */ + public static final PaperSize A5_ROTATED = new PaperSize(78); + + /** B4 (JIS) Rotated 364mm � 257mm */ + public static final PaperSize B4_ROTATED = new PaperSize(79); + + /** B5 (JIS) Rotated 257mm � 182mm */ + public static final PaperSize B5_ROTATED = new PaperSize(80); + + /** Japanese Postcard Rot. 148mm � 100mm */ + public static final PaperSize JAPANESE_POSTCARD_ROTATED = new PaperSize(81); + + /** Dbl. Jap. Postcard Rot. 148mm � 200mm */ + public static final PaperSize DOUBLE_JAPANESE_POSTCARD_ROTATED = new PaperSize(82); + + /** A6 Rotated 148mm � 105mm */ + public static final PaperSize A6_ROTATED = new PaperSize(83); + + /* 84 - 87 undefined */ + + /** B6 (JIS) 128mm � 182mm */ + public static final PaperSize B6 = new PaperSize(88); + + /** B6 (JIS) Rotated 182mm � 128mm */ + public static final PaperSize B6_ROTATED = new PaperSize(89); +} Index: 3rdParty_sources/jexcelapi/jxl/format/Pattern.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/format/Pattern.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/format/Pattern.java 17 Aug 2012 14:50:58 -0000 1.1 @@ -0,0 +1,133 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.format; + + +/** + * Enumeration class which contains the various patterns available within + * the standard Excel pattern palette + */ +public /*final*/ class Pattern +{ + /** + * The internal numerical representation of the colour + */ + private int value; + + /** + * The textual description + */ + private String string; + + /** + * The list of patterns + */ + private static Pattern[] patterns = new Pattern[0]; + + /** + * Private constructor + * + * @param val + * @param s + */ + protected Pattern(int val, String s) + { + value = val; + string = s; + + Pattern[] oldcols = patterns; + patterns = new Pattern[oldcols.length + 1]; + System.arraycopy(oldcols, 0, patterns, 0, oldcols.length); + patterns[oldcols.length] = this; + } + + /** + * Gets the value of this pattern. This is the value that is written to + * the generated Excel file + * + * @return the binary value + */ + public int getValue() + { + return value; + } + + /** + * Gets the textual description + * + * @return the string + */ + public String getDescription() + { + return string; + } + + /** + * Gets the pattern from the value + * + * @param val + * @return the pattern with that value + */ + public static Pattern getPattern(int val) + { + for (int i = 0 ; i < patterns.length ; i++) + { + if (patterns[i].getValue() == val) + { + return patterns[i]; + } + } + + return NONE; + } + + public final static Pattern NONE = new Pattern(0x0, "None"); + public final static Pattern SOLID = new Pattern(0x1, "Solid"); + + public final static Pattern GRAY_50 = new Pattern(0x2, "Gray 50%"); + public final static Pattern GRAY_75 = new Pattern(0x3, "Gray 75%"); + public final static Pattern GRAY_25 = new Pattern(0x4, "Gray 25%"); + + public final static Pattern PATTERN1 = new Pattern(0x5, "Pattern 1"); + public final static Pattern PATTERN2 = new Pattern(0x6, "Pattern 2"); + public final static Pattern PATTERN3 = new Pattern(0x7, "Pattern 3"); + public final static Pattern PATTERN4 = new Pattern(0x8, "Pattern 4"); + public final static Pattern PATTERN5 = new Pattern(0x9, "Pattern 5"); + public final static Pattern PATTERN6 = new Pattern(0xa, "Pattern 6"); + public final static Pattern PATTERN7 = new Pattern(0xb, "Pattern 7"); + public final static Pattern PATTERN8 = new Pattern(0xc, "Pattern 8"); + public final static Pattern PATTERN9 = new Pattern(0xd, "Pattern 9"); + public final static Pattern PATTERN10 = new Pattern(0xe, "Pattern 10"); + public final static Pattern PATTERN11 = new Pattern(0xf, "Pattern 11"); + public final static Pattern PATTERN12 = new Pattern(0x10, "Pattern 12"); + public final static Pattern PATTERN13 = new Pattern(0x11, "Pattern 13"); + public final static Pattern PATTERN14 = new Pattern(0x12, "Pattern 14"); +} + + + + + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/format/RGB.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/format/RGB.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/format/RGB.java 17 Aug 2012 14:50:58 -0000 1.1 @@ -0,0 +1,87 @@ +/********************************************************************* +* +* Copyright (C) 2005 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + + +package jxl.format; + +/** + * A structure which contains the RGB values for a particular colour + */ +public final class RGB +{ + /** + * The red component of this colour + */ + private int red; + + /** + * The green component of this colour + */ + private int green; + + /** + * The blue component of this colour + */ + private int blue; + + /** + * Constructor + * + * @param r the red component + * @param g the green component + * @param b the blue component + */ + public RGB(int r, int g, int b) + { + red = r; + green = g; + blue = b; + } + + /** + * Accessor for the red component + * + * @return the red component of the colour, between 0 and 255 + */ + public int getRed() + { + return red; + } + + /** + * Accessor for the green component + * + * @return the green component of the colour, between 0 and 255 + */ + public int getGreen() + { + return green; + } + + /** + * Accessor for the blue component + * + * @return the blue component of the colour, between 0 and 255 + */ + public int getBlue() + { + return blue; + } +} + Index: 3rdParty_sources/jexcelapi/jxl/format/ScriptStyle.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/format/ScriptStyle.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/format/ScriptStyle.java 17 Aug 2012 14:50:58 -0000 1.1 @@ -0,0 +1,120 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.format; + +/** + * Enumeration class which contains the various script styles available + * within the standard Excel ScriptStyle palette + * + */ +public final class ScriptStyle +{ + /** + * The internal numerical representation of the ScriptStyle + */ + private int value; + + /** + * The display string for the script style. Used when presenting the + * format information + */ + private String string; + + /** + * The list of ScriptStyles + */ + private static ScriptStyle[] styles = new ScriptStyle[0]; + + + /** + * Private constructor + * + * @param val + * @param s the display string + */ + protected ScriptStyle(int val, String s) + { + value = val; + string = s; + + ScriptStyle[] oldstyles = styles; + styles = new ScriptStyle[oldstyles.length + 1]; + System.arraycopy(oldstyles, 0, styles, 0, oldstyles.length); + styles[oldstyles.length] = this; + } + + /** + * Gets the value of this style. This is the value that is written to + * the generated Excel file + * + * @return the binary value + */ + public int getValue() + { + return value; + } + + /** + * Gets the string description for display purposes + * + * @return the string description + */ + public String getDescription() + { + return string; + } + + /** + * Gets the ScriptStyle from the value + * + * @param val + * @return the ScriptStyle with that value + */ + public static ScriptStyle getStyle(int val) + { + for (int i = 0 ; i < styles.length ; i++) + { + if (styles[i].getValue() == val) + { + return styles[i]; + } + } + + return NORMAL_SCRIPT; + } + + // The script styles + public static final ScriptStyle NORMAL_SCRIPT = new ScriptStyle(0, "normal"); + public static final ScriptStyle SUPERSCRIPT = new ScriptStyle(1, "super"); + public static final ScriptStyle SUBSCRIPT = new ScriptStyle(2, "sub"); + + +} + + + + + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/format/UnderlineStyle.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/format/UnderlineStyle.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/format/UnderlineStyle.java 17 Aug 2012 14:50:58 -0000 1.1 @@ -0,0 +1,128 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.format; + +/** + * Enumeration class which contains the various underline styles available + * within the standard Excel UnderlineStyle palette + * + */ +public final class UnderlineStyle +{ + /** + * The internal numerical representation of the UnderlineStyle + */ + private int value; + + /** + * The display string for the underline style. Used when presenting the + * format information + */ + private String string; + + /** + * The list of UnderlineStyles + */ + private static UnderlineStyle[] styles = new UnderlineStyle[0]; + + /** + * Private constructor + * + * @param val + * @param s the display string + */ + protected UnderlineStyle(int val, String s) + { + value = val; + string = s; + + UnderlineStyle[] oldstyles = styles; + styles = new UnderlineStyle[oldstyles.length + 1]; + System.arraycopy(oldstyles, 0, styles, 0, oldstyles.length); + styles[oldstyles.length] = this; + } + + /** + * Gets the value of this style. This is the value that is written to + * the generated Excel file + * + * @return the binary value + */ + public int getValue() + { + return value; + } + + /** + * Gets the string description for display purposes + * + * @return the string description + */ + public String getDescription() + { + return string; + } + + /** + * Gets the UnderlineStyle from the value + * + * @param val + * @return the UnderlineStyle with that value + */ + public static UnderlineStyle getStyle(int val) + { + for (int i = 0 ; i < styles.length ; i++) + { + if (styles[i].getValue() == val) + { + return styles[i]; + } + } + + return NO_UNDERLINE; + } + + // The underline styles + public static final UnderlineStyle NO_UNDERLINE = + new UnderlineStyle(0, "none"); + + public static final UnderlineStyle SINGLE = + new UnderlineStyle(1, "single"); + + public static final UnderlineStyle DOUBLE = + new UnderlineStyle(2, "double"); + + public static final UnderlineStyle SINGLE_ACCOUNTING = + new UnderlineStyle(0x21, "single accounting"); + + public static final UnderlineStyle DOUBLE_ACCOUNTING = + new UnderlineStyle(0x22, "double accounting"); +} + + + + + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/format/VerticalAlignment.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/format/VerticalAlignment.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/format/VerticalAlignment.java 17 Aug 2012 14:50:58 -0000 1.1 @@ -0,0 +1,117 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.format; + +/** + * Enumeration type which describes the vertical alignment of data within a cell + */ +public /*final*/ class VerticalAlignment +{ + /** + * The internal binary value which gets written to the generated Excel file + */ + private int value; + + /** + * The textual description + */ + private String string; + + /** + * The list of alignments + */ + private static VerticalAlignment[] alignments = new VerticalAlignment[0]; + + /** + * Constructor + * + * @param val + */ + protected VerticalAlignment(int val, String s) + { + value = val; + string = s; + + VerticalAlignment[] oldaligns = alignments; + alignments = new VerticalAlignment[oldaligns.length + 1]; + System.arraycopy(oldaligns, 0, alignments, 0, oldaligns.length); + alignments[oldaligns.length] = this; + } + + /** + * Accessor for the binary value + * + * @return the internal binary value + */ + public int getValue() + { + return value; + } + + /** + * Gets the textual description + */ + public String getDescription() + { + return string; + } + + /** + * Gets the alignment from the value + * + * @param val + * @return the alignment with that value + */ + public static VerticalAlignment getAlignment(int val) + { + for (int i = 0 ; i < alignments.length ; i++) + { + if (alignments[i].getValue() == val) + { + return alignments[i]; + } + } + + return BOTTOM; + } + + + /** + * Cells with this specified vertical alignment will have their data + * aligned at the top + */ + public static VerticalAlignment TOP = new VerticalAlignment(0, "top"); + /** + * Cells with this specified vertical alignment will have their data + * aligned centrally + */ + public static VerticalAlignment CENTRE = new VerticalAlignment(1, "centre"); + /** + * Cells with this specified vertical alignment will have their data + * aligned at the bottom + */ + public static VerticalAlignment BOTTOM = new VerticalAlignment(2, "bottom"); + /** + * Cells with this specified vertical alignment will have their data + * justified + */ + public static VerticalAlignment JUSTIFY = new VerticalAlignment(3, "Justify"); +} + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/BOFRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/BOFRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/BOFRecord.java 17 Aug 2012 14:50:57 -0000 1.1 @@ -0,0 +1,170 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import common.Logger; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; + +/** + * A Beginning Of File record, found at the commencement of all substreams + * within a biff8 file + */ +public class BOFRecord extends RecordData +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(BOFRecord.class); + + /** + * The code used for biff8 files + */ + private static final int Biff8 = 0x600; + /** + * The code used for biff8 files + */ + private static final int Biff7 = 0x500; + /** + * The code used for workbook globals + */ + private static final int WorkbookGlobals = 0x5; + /** + * The code used for worksheets + */ + private static final int Worksheet = 0x10; + /** + * The code used for charts + */ + private static final int Chart = 0x20; + /** + * The code used for macro sheets + */ + private static final int MacroSheet = 0x40; + + /** + * The biff version of this substream + */ + private int version; + /** + * The type of this substream + */ + private int substreamType; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + */ + BOFRecord(Record t) + { + super(t); + byte[] data = getRecord().getData(); + version = IntegerHelper.getInt(data[0], data[1]); + substreamType = IntegerHelper.getInt(data[2], data[3]); + } + + /** + * Interrogates this object to see if it is a biff8 substream + * + * @return TRUE if this substream is biff8, false otherwise + */ + public boolean isBiff8() + { + return version == Biff8; + } + + /** + * Interrogates this object to see if it is a biff7 substream + * + * @return TRUE if this substream is biff7, false otherwise + */ + public boolean isBiff7() + { + return version == Biff7; + } + + + /** + * Interrogates this substream to see if it represents the commencement of + * the workbook globals substream + * + * @return TRUE if this is the commencement of a workbook globals substream, + * FALSE otherwise + */ + boolean isWorkbookGlobals() + { + return substreamType == WorkbookGlobals; + } + + /** + * Interrogates the substream to see if it is the commencement of a worksheet + * + * @return TRUE if this substream is the beginning of a worksheet, FALSE + * otherwise + */ + public boolean isWorksheet() + { + return substreamType == Worksheet; + } + + /** + * Interrogates the substream to see if it is the commencement of a worksheet + * + * @return TRUE if this substream is the beginning of a worksheet, FALSE + * otherwise + */ + public boolean isMacroSheet() + { + return substreamType == MacroSheet; + } + + /** + * Interrogates the substream to see if it is a chart + * + * @return TRUE if this substream is the beginning of a worksheet, FALSE + * otherwise + */ + public boolean isChart() + { + return substreamType == Chart; + } + + /** + * Gets the length of the data portion of this record + * Used to adjust when reading sheets which contain just a chart + * @return the length of the data portion of this record + */ + int getLength() + { + return getRecord().getLength(); + } + +} + + + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/BaseSharedFormulaRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/BaseSharedFormulaRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/BaseSharedFormulaRecord.java 17 Aug 2012 14:50:57 -0000 1.1 @@ -0,0 +1,174 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; + +/** + * A base class for shared formula records + */ +public abstract class BaseSharedFormulaRecord extends CellValue + implements FormulaData +{ + /** + * The formula as an excel string + */ + private String formulaString; + + /** + * The position of the next record in the file. Used when looking for + * for subsequent records eg. a string value + */ + private int filePos; + + /** + * The array of parsed tokens + */ + private byte[] tokens; + + /** + * The external sheet + */ + private ExternalSheet externalSheet; + + /** + * The name table + */ + private WorkbookMethods nameTable; + + /** + * Constructs this number + * + * @param t the record + * @param fr the formatting records + * @param es the external sheet + * @param nt the name table + * @param si the sheet + * @param pos the position of the next record in the file + */ + public BaseSharedFormulaRecord(Record t, + FormattingRecords fr, + ExternalSheet es, + WorkbookMethods nt, + SheetImpl si, + int pos) + { + super(t, fr, si); + externalSheet = es; + nameTable = nt; + filePos = pos; + } + + /** + * Gets the formula as an excel string + * + * @return the formula as an excel string + * @exception FormulaException + */ + public String getFormula() throws FormulaException + { + if (formulaString == null) + { + FormulaParser fp = new FormulaParser + (tokens, this, externalSheet, nameTable, + getSheet().getWorkbook().getSettings()); + fp.parse(); + formulaString = fp.getFormula(); + } + + return formulaString; + } + + /** + * Called by the shared formula record to set the tokens for + * this formula + * + * @param t the tokens + */ + void setTokens(byte[] t) + { + tokens = t; + } + + /** + * Accessor for the tokens which make up this formula + * + * @return the tokens + */ + protected final byte[] getTokens() + { + return tokens; + } + + /** + * Access for the external sheet + * + * @return the external sheet + */ + protected final ExternalSheet getExternalSheet() + { + return externalSheet; + } + + /** + * Access for the name table + * + * @return the name table + */ + protected final WorkbookMethods getNameTable() + { + return nameTable; + } + + /** + * In case the shared formula is not added for any reason, we need + * to expose the raw record data , in order to try again + * + * @return the record data from the base class + */ + public Record getRecord() + { + return super.getRecord(); + } + + /** + * Accessor for the position of the next record + * + * @return the position of the next record + */ + final int getFilePos() + { + return filePos; + } +} + + + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/BiffException.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/BiffException.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/BiffException.java 17 Aug 2012 14:50:56 -0000 1.1 @@ -0,0 +1,99 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import jxl.JXLException; + +/** + * Exception thrown when reading a biff file + */ +public class BiffException extends JXLException +{ + /** + * Inner class containing the various error messages + */ + private static class BiffMessage + { + /** + * The formatted message + */ + public String message; + /** + * Constructs this exception with the specified message + * + * @param m the messageA + */ + BiffMessage(String m) + { + message = m; + } + } + + /** + */ + static final BiffMessage unrecognizedBiffVersion = + new BiffMessage("Unrecognized biff version"); + + /** + */ + static final BiffMessage expectedGlobals = + new BiffMessage("Expected globals"); + + /** + */ + static final BiffMessage excelFileTooBig = + new BiffMessage("Warning: not all of the excel file could be read"); + + /** + */ + static final BiffMessage excelFileNotFound = + new BiffMessage("The input file was not found"); + + /** + */ + static final BiffMessage unrecognizedOLEFile = + new BiffMessage("Unable to recognize OLE stream"); + + /** + */ + static final BiffMessage streamNotFound = + new BiffMessage("Compound file does not contain the specified stream"); + + /** + */ + static final BiffMessage passwordProtected = + new BiffMessage("The workbook is password protected"); + + /** + */ + static final BiffMessage corruptFileFormat = + new BiffMessage("The file format is corrupt"); + + + /** + * Constructs this exception with the specified message + * + * @param m the message + */ + public BiffException(BiffMessage m) + { + super(m.message); + } +} Index: 3rdParty_sources/jexcelapi/jxl/read/biff/BiffRecordReader.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/BiffRecordReader.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/BiffRecordReader.java 17 Aug 2012 14:50:55 -0000 1.1 @@ -0,0 +1,79 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +/** + * Serves up Record objects from a biff file. This object is used by the + * demo programs BiffDump and ... only and has no influence whatsoever on + * the JExcelApi reading and writing of excel sheets + */ +public class BiffRecordReader +{ + /** + * The biff file + */ + private File file; + + /** + * The current record retrieved + */ + private Record record; + + /** + * Constructor + * + * @param f the biff file + */ + public BiffRecordReader(File f) + { + file = f; + } + + /** + * Sees if there are any more records to read + * + * @return TRUE if there are more records, FALSE otherwise + */ + public boolean hasNext() + { + return file.hasNext(); + } + + /** + * Gets the next record + * + * @return the next record + */ + public Record next() + { + record = file.next(); + return record; + } + + /** + * Gets the position of the current record in the biff file + * + * @return the position + */ + public int getPos() + { + return file.getPos() - record.getLength() - 4; + } +} Index: 3rdParty_sources/jexcelapi/jxl/read/biff/BlankCell.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/BlankCell.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/BlankCell.java 17 Aug 2012 14:50:56 -0000 1.1 @@ -0,0 +1,65 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import jxl.CellType; +import jxl.biff.FormattingRecords; + +/** + * A blank cell. Despite the fact that this cell has no contents, it + * has formatting information applied to it + */ +public class BlankCell extends CellValue +{ + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param fr the available formats + * @param si the sheet + */ + BlankCell(Record t, FormattingRecords fr, SheetImpl si) + { + super(t, fr, si); + } + + /** + * Returns the contents of this cell as an empty string + * + * @return the value formatted into a string + */ + public String getContents() + { + return ""; + } + + /** + * Accessor for the cell type + * + * @return the cell type + */ + public CellType getType() + { + return CellType.EMPTY; + } +} + + + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/BooleanFormulaRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/BooleanFormulaRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/BooleanFormulaRecord.java 17 Aug 2012 14:50:58 -0000 1.1 @@ -0,0 +1,167 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import common.Assert; + +import jxl.BooleanCell; +import jxl.BooleanFormulaCell; +import jxl.CellType; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; + +/** + * A boolean formula's last calculated value + */ +class BooleanFormulaRecord extends CellValue + implements BooleanCell, FormulaData, BooleanFormulaCell +{ + /** + * The boolean value of this cell. If this cell represents an error, + * this will be false + */ + private boolean value; + + /** + * A handle to the class needed to access external sheets + */ + private ExternalSheet externalSheet; + + /** + * A handle to the name table + */ + private WorkbookMethods nameTable; + + /** + * The formula as an excel string + */ + private String formulaString; + + /** + * The raw data + */ + private byte[] data; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param fr the formatting records + * @param si the sheet + * @param es the sheet + * @param nt the name table + */ + public BooleanFormulaRecord(Record t, FormattingRecords fr, + ExternalSheet es, WorkbookMethods nt, + SheetImpl si) + { + super(t, fr, si); + externalSheet = es; + nameTable = nt; + value = false; + + data = getRecord().getData(); + + Assert.verify(data[6] != 2); + + value = data[8] == 1 ? true : false; + } + + /** + * Interface method which Gets the boolean value stored in this cell. If + * this cell contains an error, then returns FALSE. Always query this cell + * type using the accessor method isError() prior to calling this method + * + * @return TRUE if this cell contains TRUE, FALSE if it contains FALSE or + * an error code + */ + public boolean getValue() + { + return value; + } + + /** + * Returns the numerical value as a string + * + * @return The numerical value of the formula as a string + */ + public String getContents() + { + // return Boolean.toString(value) - only available in 1.4 or later + return (new Boolean(value)).toString(); + } + + /** + * Returns the cell type + * + * @return The cell type + */ + public CellType getType() + { + return CellType.BOOLEAN_FORMULA; + } + + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array + * + * @return the raw record data + */ + public byte[] getFormulaData() throws FormulaException + { + if (!getSheet().getWorkbookBof().isBiff8()) + { + throw new FormulaException(FormulaException.BIFF8_SUPPORTED); + } + + // Lop off the standard information + byte[] d = new byte[data.length - 6]; + System.arraycopy(data, 6, d, 0, data.length - 6); + + return d; + } + + /** + * Gets the formula as an excel string + * + * @return the formula as an excel string + * @exception FormulaException + */ + public String getFormula() throws FormulaException + { + if (formulaString == null) + { + byte[] tokens = new byte[data.length - 22]; + System.arraycopy(data, 22, tokens, 0, tokens.length); + FormulaParser fp = new FormulaParser + (tokens, this, externalSheet, nameTable, + getSheet().getWorkbook().getSettings()); + fp.parse(); + formulaString = fp.getFormula(); + } + + return formulaString; + } +} + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/BooleanRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/BooleanRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/BooleanRecord.java 17 Aug 2012 14:50:57 -0000 1.1 @@ -0,0 +1,130 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import common.Assert; + +import jxl.BooleanCell; +import jxl.CellType; +import jxl.biff.FormattingRecords; + +/** + * A boolean cell last calculated value + */ +class BooleanRecord extends CellValue implements BooleanCell +{ + /** + * Indicates whether this cell contains an error or a boolean + */ + private boolean error; + + /** + * The boolean value of this cell. If this cell represents an error, + * this will be false + */ + private boolean value; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param fr the formatting records + * @param si the sheet + */ + public BooleanRecord(Record t, FormattingRecords fr, SheetImpl si) + { + super(t, fr, si); + error = false; + value = false; + + byte[] data = getRecord().getData(); + + error = (data[7] == 1); + + if (!error) + { + value = data[6] == 1 ? true : false; + } + } + + /** + * Interface method which queries whether this cell contains an error. + * Returns TRUE if it does, otherwise returns FALSE. + * + * @return TRUE if this cell is an error, FALSE otherwise + */ + public boolean isError() + { + return error; + } + + /** + * Interface method which Gets the boolean value stored in this cell. If + * this cell contains an error, then returns FALSE. Always query this cell + * type using the accessor method isError() prior to calling this method + * + * @return TRUE if this cell contains TRUE, FALSE if it contains FALSE or + * an error code + */ + public boolean getValue() + { + return value; + } + + /** + * Returns the numerical value as a string + * + * @return The numerical value of the formula as a string + */ + public String getContents() + { + Assert.verify(!isError()); + + // return Boolean.toString(value) - only available in 1.4 or later + return (new Boolean(value)).toString(); + } + + /** + * Returns the cell type + * + * @return The cell type + */ + public CellType getType() + { + return CellType.BOOLEAN; + } + + /** + * A special case which overrides the method in the subclass to get + * hold of the raw data + * + * @return the record + */ + public Record getRecord() + { + return super.getRecord(); + } +} + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/BottomMarginRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/BottomMarginRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/BottomMarginRecord.java 17 Aug 2012 14:50:57 -0000 1.1 @@ -0,0 +1,38 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.Type; + +/** + * Record for the left margin settings + */ +class BottomMarginRecord extends MarginRecord +{ + /** + * Constructor + * + * @param r the record + */ + BottomMarginRecord(Record r) + { + super(Type.BOTTOMMARGIN, r); + } +} Index: 3rdParty_sources/jexcelapi/jxl/read/biff/BoundsheetRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/BoundsheetRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/BoundsheetRecord.java 17 Aug 2012 14:50:55 -0000 1.1 @@ -0,0 +1,156 @@ +/********************************************************************* +* +* Copyright (C) 2001 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import jxl.WorkbookSettings; +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.StringHelper; + +/** + * A boundsheet record, which contains the worksheet name + */ +class BoundsheetRecord extends RecordData +{ + /** + * The offset into the sheet + */ + private int offset; + /** + * The type of sheet this is + */ + private byte typeFlag; + /** + * The visibility flag + */ + private byte visibilityFlag; + /** + * The length of the worksheet name + */ + private int length; + /** + * The worksheet name + */ + private String name; + + /** + * Dummy indicators for overloading the constructor + */ + private static class Biff7 {}; + public static Biff7 biff7 = new Biff7(); + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param s the workbook settings + */ + public BoundsheetRecord(Record t, WorkbookSettings s) + { + super(t); + byte[] data = getRecord().getData(); + offset = IntegerHelper.getInt(data[0], data[1], data[2], data[3]); + typeFlag = data[5]; + visibilityFlag = data[4]; + length = data[6]; + + if (data[7] == 0) + { + // Standard ASCII encoding + byte[] bytes = new byte[length]; + System.arraycopy(data, 8, bytes, 0, length); + name = StringHelper.getString(bytes, length, 0, s); + } + else + { + // little endian Unicode encoding + byte[] bytes = new byte[length * 2]; + System.arraycopy(data, 8, bytes, 0, length * 2); + name = StringHelper.getUnicodeString(bytes, length, 0); + } + } + + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param biff7 a dummy value to tell the record to interpret the + * data as biff7 + */ + public BoundsheetRecord(Record t, Biff7 biff7) + { + super(t); + byte[] data = getRecord().getData(); + offset = IntegerHelper.getInt(data[0], data[1], data[2], data[3]); + typeFlag = data[5]; + visibilityFlag = data[4]; + length = data[6]; + byte[] bytes = new byte[length]; + System.arraycopy(data, 7, bytes, 0, length); + name = new String(bytes); + } + + /** + * Accessor for the worksheet name + * + * @return the worksheet name + */ + public String getName() + { + return name; + } + + /** + * Accessor for the hidden flag + * + * @return TRUE if this is a hidden sheet, FALSE otherwise + */ + public boolean isHidden() + { + return visibilityFlag != 0; + } + + /** + * Accessor to determine if this is a worksheet, or some other nefarious + * type of object + * + * @return TRUE if this is a worksheet, FALSE otherwise + */ + public boolean isSheet() + { + return typeFlag == 0; + } + + /** + * Accessor to determine if this is a chart + * + * @return TRUE if this is a chart, FALSE otherwise + */ + public boolean isChart() + { + return typeFlag == 2; + } + +} + + + + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/ButtonPropertySetRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/ButtonPropertySetRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/ButtonPropertySetRecord.java 17 Aug 2012 14:50:57 -0000 1.1 @@ -0,0 +1,57 @@ +/********************************************************************* +* +* Copyright (C) 2004 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import common.Logger; + +import jxl.biff.RecordData; + +/** + * Data associated with a button property set + */ +public class ButtonPropertySetRecord extends RecordData +{ + /** + * The logger + */ + private static Logger logger = + Logger.getLogger(ButtonPropertySetRecord.class); + + + /** + * Constructor + * + * @param t the record + */ + ButtonPropertySetRecord(Record t) + { + super(t); + } + + /** + * Accessor for the binary data + * + * @return the binary data + */ + public byte[] getData() + { + return getRecord().getData(); + } +} Index: 3rdParty_sources/jexcelapi/jxl/read/biff/CalcModeRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/CalcModeRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/CalcModeRecord.java 17 Aug 2012 14:50:56 -0000 1.1 @@ -0,0 +1,64 @@ +/********************************************************************* +* +* Copyright (C) 2005 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import common.Logger; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; + +/** + * A calculation mode record + */ +class CalcModeRecord extends RecordData +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(CalcModeRecord.class); + + /** + * The calculation mode + */ + private boolean automatic; + + /** + * Constructor + * + * @param t the record + */ + public CalcModeRecord(Record t) + { + super(t); + byte[] data = t.getData(); + int mode = IntegerHelper.getInt(data[0], data[1]); + automatic = (mode == 1); + } + + /** + * Accessor for the calculation mode + * + * @return the calculation mode + */ + public boolean isAutomatic() + { + return automatic; + } +} Index: 3rdParty_sources/jexcelapi/jxl/read/biff/CellFeaturesAccessor.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/CellFeaturesAccessor.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/CellFeaturesAccessor.java 17 Aug 2012 14:50:58 -0000 1.1 @@ -0,0 +1,42 @@ +/********************************************************************* +* +* Copyright (C) 2005 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import jxl.CellFeatures; + +/** + * Allows the setting of the cell features in this package only + */ +interface CellFeaturesAccessor +{ + /** + * Sets the cell features + * + * @param cf the cell features + */ + public void setCellFeatures(CellFeatures cf); + + /** + * Convenience function (due to casting) to get the cell features + * + * @return the cell features + */ + public CellFeatures getCellFeatures(); +} Index: 3rdParty_sources/jexcelapi/jxl/read/biff/CellValue.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/CellValue.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/CellValue.java 17 Aug 2012 14:50:56 -0000 1.1 @@ -0,0 +1,210 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import common.Logger; + +import jxl.Cell; +import jxl.CellFeatures; +import jxl.biff.FormattingRecords; +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.XFRecord; +import jxl.format.CellFormat; + +/** + * Abstract class for all records which actually contain cell values + */ +public abstract class CellValue extends RecordData + implements Cell, CellFeaturesAccessor +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(CellValue.class); + + /** + * The row number of this cell record + */ + private int row; + + /** + * The column number of this cell record + */ + private int column; + + /** + * The XF index + */ + private int xfIndex; + + /** + * A handle to the formatting records, so that we can + * retrieve the formatting information + */ + private FormattingRecords formattingRecords; + + /** + * A lazy initialize flag for the cell format + */ + private boolean initialized; + + /** + * The cell format + */ + private XFRecord format; + + /** + * A handle back to the sheet + */ + private SheetImpl sheet; + + /** + * The cell features + */ + private CellFeatures features; + + /** + * Constructs this object from the raw cell data + * + * @param t the raw cell data + * @param fr the formatting records + * @param si the sheet containing this cell + */ + protected CellValue(Record t, FormattingRecords fr, SheetImpl si) + { + super(t); + byte[] data = getRecord().getData(); + row = IntegerHelper.getInt(data[0], data[1]); + column = IntegerHelper.getInt(data[2], data[3]); + xfIndex = IntegerHelper.getInt(data[4], data[5]); + sheet = si; + formattingRecords = fr; + initialized = false; + } + + /** + * Interface method which returns the row number of this cell + * + * @return the zero base row number + */ + public final int getRow() + { + return row; + } + + /** + * Interface method which returns the column number of this cell + * + * @return the zero based column number + */ + public final int getColumn() + { + return column; + } + + /** + * Gets the XFRecord corresponding to the index number. Used when + * copying a spreadsheet + * + * @return the xf index for this cell + */ + public final int getXFIndex() + { + return xfIndex; + } + + /** + * Gets the CellFormat object for this cell. Used by the WritableWorkbook + * API + * + * @return the CellFormat used for this cell + */ + public CellFormat getCellFormat() + { + if (!initialized) + { + format = formattingRecords.getXFRecord(xfIndex); + initialized = true; + } + + return format; + } + + /** + * Determines whether or not this cell has been hidden + * + * @return TRUE if this cell has been hidden, FALSE otherwise + */ + public boolean isHidden() + { + ColumnInfoRecord cir = sheet.getColumnInfo(column); + + if (cir != null && (cir.getWidth() == 0 || cir.getHidden())) + { + return true; + } + + RowRecord rr = sheet.getRowInfo(row); + + if (rr != null && (rr.getRowHeight() == 0 || rr.isCollapsed())) + { + return true; + } + + return false; + } + + /** + * Accessor for the sheet + * + * @return the sheet + */ + protected SheetImpl getSheet() + { + return sheet; + } + + /** + * Accessor for the cell features + * + * @return the cell features or NULL if this cell doesn't have any + */ + public CellFeatures getCellFeatures() + { + return features; + } + + /** + * Sets the cell features during the reading process + * + * @param cf the cell features + */ + public void setCellFeatures(CellFeatures cf) + { + if (features != null) + { + logger.warn("current cell features not null - overwriting"); + } + + features = cf; + } +} + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/CentreRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/CentreRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/CentreRecord.java 17 Aug 2012 14:50:56 -0000 1.1 @@ -0,0 +1,56 @@ +/********************************************************************* +* +* Copyright (C) 2005 Andrew Khan, Adam Caldwell +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; + +/** + * Record which indicates the whether the horizontal center option has been set + */ +class CentreRecord extends RecordData +{ + /** + * The centre flag + */ + private boolean centre; + + /** + * Constructor + * + * @param t the record to constructfrom + */ + public CentreRecord(Record t) + { + super(t); + byte[] data = getRecord().getData(); + centre = IntegerHelper.getInt(data[0], data[1]) != 0; + } + + /** + * Accessor for the centre flag + * + * @return Returns the centre flag. + */ + public boolean isCentre() + { + return centre; + } +} Index: 3rdParty_sources/jexcelapi/jxl/read/biff/CodepageRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/CodepageRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/CodepageRecord.java 17 Aug 2012 14:50:55 -0000 1.1 @@ -0,0 +1,63 @@ +/********************************************************************* +* +* Copyright (C) 2004 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import common.Logger; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; + +/** + * A codepage record + */ +class CodepageRecord extends RecordData +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(CodepageRecord.class); + + /** + * The character encoding + */ + private int characterSet; + + /** + * Constructor + * + * @param t the record + */ + public CodepageRecord(Record t) + { + super(t); + byte[] data = t.getData(); + characterSet = IntegerHelper.getInt(data[0], data[1]); + } + + /** + * Accessor for the encoding + * + * @return the character encoding + */ + public int getCharacterSet() + { + return characterSet; + } +} Index: 3rdParty_sources/jexcelapi/jxl/read/biff/ColumnInfoRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/ColumnInfoRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/ColumnInfoRecord.java 17 Aug 2012 14:50:56 -0000 1.1 @@ -0,0 +1,164 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.Type; + +/** + * Contains the display info data which affects the entire columns + */ +public class ColumnInfoRecord extends RecordData +{ + /** + * The raw data + */ + private byte[] data; + + /** + * The start for which to apply the format information + */ + private int startColumn; + + /** + * The end column for which to apply the format information + */ + private int endColumn; + + /** + * The index to the XF record, which applies to each cell in this column + */ + private int xfIndex; + + /** + * The width of the column in 1/256 of a character + */ + private int width; + + /** + * A hidden flag + */ + private boolean hidden; + + /** + * The column's outline level + */ + private int outlineLevel; + + /** + * The column collapsed flag + */ + private boolean collapsed; + + /** + * Constructor which creates this object from the binary data + * + * @param t the record + */ + ColumnInfoRecord(Record t) + { + super(Type.COLINFO); + + data = t.getData(); + + startColumn = IntegerHelper.getInt(data[0], data[1]); + endColumn = IntegerHelper.getInt(data[2], data[3]); + width = IntegerHelper.getInt(data[4], data[5]); + xfIndex = IntegerHelper.getInt(data[6], data[7]); + + int options = IntegerHelper.getInt(data[8], data[9]); + hidden = ((options & 0x1) != 0); + outlineLevel = ((options & 0x700) >> 8); + collapsed = ((options & 0x1000) != 0); + } + + /** + * Accessor for the start column of this range + * + * @return the start column index + */ + public int getStartColumn() + { + return startColumn; + } + + /** + * Accessor for the end column of this range + * + * @return the end column index + */ + public int getEndColumn() + { + return endColumn; + } + + /** + * Accessor for the column format index + * + * @return the format index + */ + public int getXFIndex() + { + return xfIndex; + } + + /** + * Accessor for the column's outline level + * + * @return the column's outline level + */ + public int getOutlineLevel() + { + return outlineLevel; + } + + /** + * Accessor for whether the column is collapsed + * + * @return the column's collapsed state + */ + public boolean getCollapsed() + { + return collapsed; + } + + /** + * Accessor for the width of the column + * + * @return the width + */ + public int getWidth() + { + return width; + } + + /** + * Accessor for the hidden flag. Used when copying sheets + * + * @return TRUE if the columns are hidden, FALSE otherwise + */ + public boolean getHidden() + { + return hidden; + } +} + + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/CompoundFile.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/CompoundFile.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/CompoundFile.java 17 Aug 2012 14:50:56 -0000 1.1 @@ -0,0 +1,609 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import java.util.ArrayList; +import java.util.Iterator; + +import common.Logger; + +import jxl.WorkbookSettings; +import jxl.biff.BaseCompoundFile; +import jxl.biff.IntegerHelper; + +/** + * Reads in and defrags an OLE compound compound file + * (Made public only for the PropertySets demo) + */ +public final class CompoundFile extends BaseCompoundFile +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(CompoundFile.class); + + /** + * The original OLE stream, organized into blocks, which can + * appear at any physical location in the file + */ + private byte[] data; + /** + * The number of blocks it takes to store the big block depot + */ + private int numBigBlockDepotBlocks; + /** + * The start block of the small block depot + */ + private int sbdStartBlock; + /** + * The start block of the root entry + */ + private int rootStartBlock; + /** + * The header extension block + */ + private int extensionBlock; + /** + * The number of header extension blocks + */ + private int numExtensionBlocks; + /** + * The root entry + */ + private byte[] rootEntry; + /** + * The sequence of blocks which comprise the big block chain + */ + private int[] bigBlockChain; + /** + * The sequence of blocks which comprise the small block chain + */ + private int[] smallBlockChain; + /** + * The chain of blocks which comprise the big block depot + */ + private int[] bigBlockDepotBlocks; + /** + * The list of property sets + */ + private ArrayList propertySets; + + /** + * The workbook settings + */ + private WorkbookSettings settings; + + /** + * The property storage root entry + */ + private PropertyStorage rootEntryPropertyStorage; + + /** + * Initializes the compound file + * + * @param d the raw data of the ole stream + * @param ws the workbook settings + * @exception BiffException + */ + public CompoundFile(byte[] d, WorkbookSettings ws) throws BiffException + { + super(); + data = d; + settings = ws; + + // First verify the OLE identifier + for (int i = 0; i < IDENTIFIER.length; i++) + { + if (data[i] != IDENTIFIER[i]) + { + throw new BiffException(BiffException.unrecognizedOLEFile); + } + } + + propertySets = new ArrayList(); + numBigBlockDepotBlocks = IntegerHelper.getInt + (data[NUM_BIG_BLOCK_DEPOT_BLOCKS_POS], + data[NUM_BIG_BLOCK_DEPOT_BLOCKS_POS + 1], + data[NUM_BIG_BLOCK_DEPOT_BLOCKS_POS + 2], + data[NUM_BIG_BLOCK_DEPOT_BLOCKS_POS + 3]); + sbdStartBlock = IntegerHelper.getInt + (data[SMALL_BLOCK_DEPOT_BLOCK_POS], + data[SMALL_BLOCK_DEPOT_BLOCK_POS + 1], + data[SMALL_BLOCK_DEPOT_BLOCK_POS + 2], + data[SMALL_BLOCK_DEPOT_BLOCK_POS + 3]); + rootStartBlock = IntegerHelper.getInt + (data[ROOT_START_BLOCK_POS], + data[ROOT_START_BLOCK_POS + 1], + data[ROOT_START_BLOCK_POS + 2], + data[ROOT_START_BLOCK_POS + 3]); + extensionBlock = IntegerHelper.getInt + (data[EXTENSION_BLOCK_POS], + data[EXTENSION_BLOCK_POS + 1], + data[EXTENSION_BLOCK_POS + 2], + data[EXTENSION_BLOCK_POS + 3]); + numExtensionBlocks = IntegerHelper.getInt + (data[NUM_EXTENSION_BLOCK_POS], + data[NUM_EXTENSION_BLOCK_POS + 1], + data[NUM_EXTENSION_BLOCK_POS + 2], + data[NUM_EXTENSION_BLOCK_POS + 3]); + + bigBlockDepotBlocks = new int[numBigBlockDepotBlocks]; + + int pos = BIG_BLOCK_DEPOT_BLOCKS_POS; + + int bbdBlocks = numBigBlockDepotBlocks; + + if (numExtensionBlocks != 0) + { + bbdBlocks = (BIG_BLOCK_SIZE - BIG_BLOCK_DEPOT_BLOCKS_POS) / 4; + } + + for (int i = 0; i < bbdBlocks; i++) + { + bigBlockDepotBlocks[i] = IntegerHelper.getInt + (d[pos], d[pos + 1], d[pos + 2], d[pos + 3]); + pos += 4; + } + + for (int j = 0; j < numExtensionBlocks; j++) + { + pos = (extensionBlock + 1) * BIG_BLOCK_SIZE; + int blocksToRead = Math.min(numBigBlockDepotBlocks - bbdBlocks, + BIG_BLOCK_SIZE / 4 - 1); + + for (int i = bbdBlocks; i < bbdBlocks + blocksToRead; i++) + { + bigBlockDepotBlocks[i] = IntegerHelper.getInt + (d[pos], d[pos + 1], d[pos + 2], d[pos + 3]); + pos += 4; + } + + bbdBlocks += blocksToRead; + if (bbdBlocks < numBigBlockDepotBlocks) + { + extensionBlock = IntegerHelper.getInt + (d[pos], d[pos + 1], d[pos + 2], d[pos + 3]); + } + } + + readBigBlockDepot(); + readSmallBlockDepot(); + + rootEntry = readData(rootStartBlock); + readPropertySets(); + } + + /** + * Reads the big block depot entries + */ + private void readBigBlockDepot() + { + int pos = 0; + int index = 0; + bigBlockChain = new int[numBigBlockDepotBlocks * BIG_BLOCK_SIZE / 4]; + + for (int i = 0; i < numBigBlockDepotBlocks; i++) + { + pos = (bigBlockDepotBlocks[i] + 1) * BIG_BLOCK_SIZE; + + for (int j = 0; j < BIG_BLOCK_SIZE / 4; j++) + { + bigBlockChain[index] = IntegerHelper.getInt + (data[pos], data[pos + 1], data[pos + 2], data[pos + 3]); + pos += 4; + index++; + } + } + } + + /** + * Reads the small block chain's depot entries + */ + private void readSmallBlockDepot() + { + int pos = 0; + int index = 0; + int sbdBlock = sbdStartBlock; + smallBlockChain = new int[0]; + + // Some non-excel generators specify -1 for an empty small block depot + // simply warn and return + if (sbdBlock == -1) + { + logger.warn("invalid small block depot number"); + return; + } + + while (sbdBlock != -2) + { + // Allocate some more space to the small block chain + int[] oldChain = smallBlockChain; + smallBlockChain = new int[smallBlockChain.length + BIG_BLOCK_SIZE / 4]; + System.arraycopy(oldChain, 0, smallBlockChain, 0, oldChain.length); + + pos = (sbdBlock + 1) * BIG_BLOCK_SIZE; + + for (int j = 0; j < BIG_BLOCK_SIZE / 4; j++) + { + smallBlockChain[index] = IntegerHelper.getInt + (data[pos], data[pos + 1], data[pos + 2], data[pos + 3]); + pos += 4; + index++; + } + + sbdBlock = bigBlockChain[sbdBlock]; + } + } + + /** + * Reads all the property sets + */ + private void readPropertySets() + { + int offset = 0; + byte[] d = null; + + while (offset < rootEntry.length) + { + d = new byte[PROPERTY_STORAGE_BLOCK_SIZE]; + System.arraycopy(rootEntry, offset, d, 0, d.length); + PropertyStorage ps = new PropertyStorage(d); + + // sometimes the MAC Operating system leaves some property storage + // names blank. Contributed by Jacky + if (ps.name == null || ps.name.length() == 0) + { + if (ps.type == ROOT_ENTRY_PS_TYPE) + { + ps.name = ROOT_ENTRY_NAME; + logger.warn("Property storage name for " + ps.type + + " is empty - setting to " + ROOT_ENTRY_NAME); + } + else + { + if (ps.size != 0) + { + logger.warn("Property storage type " + ps.type + + " is non-empty and has no associated name"); + } + } + } + propertySets.add(ps); + if (ps.name.equalsIgnoreCase(ROOT_ENTRY_NAME)) + { + rootEntryPropertyStorage = ps; + } + offset += PROPERTY_STORAGE_BLOCK_SIZE; + } + + if (rootEntryPropertyStorage == null) + { + rootEntryPropertyStorage = (PropertyStorage) propertySets.get(0); + } + } + + /** + * Gets the defragmented stream from this ole compound file + * + * @param streamName the stream name to get + * @return the defragmented ole stream + * @exception BiffException + */ + public byte[] getStream(String streamName) throws BiffException + { + PropertyStorage ps = findPropertyStorage(streamName, + rootEntryPropertyStorage); + + // Property set can't be found from the direct hierarchy, so just + // search on the name + if (ps == null) + { + ps = getPropertyStorage(streamName); + } + + if (ps.size >= SMALL_BLOCK_THRESHOLD || + streamName.equalsIgnoreCase(ROOT_ENTRY_NAME)) + { + return getBigBlockStream(ps); + } + else + { + return getSmallBlockStream(ps); + } + } + + /** + * Gets the defragmented stream from this ole compound file. Used when + * copying workbooks with macros + * + * @param psIndex the property storage index + * @return the defragmented ole stream + * @exception BiffException + */ + public byte[] getStream(int psIndex) throws BiffException + { + PropertyStorage ps = getPropertyStorage(psIndex); + + if (ps.size >= SMALL_BLOCK_THRESHOLD || + ps.name.equalsIgnoreCase(ROOT_ENTRY_NAME)) + { + return getBigBlockStream(ps); + } + else + { + return getSmallBlockStream(ps); + } + } + + /** + * Recursively searches the property storages in hierarchy order + * for the appropriate name. This is the public version which is + * invoked from the writable version + * when copying a sheet with addition property sets. + */ + public PropertyStorage findPropertyStorage(String name) + { + return findPropertyStorage(name, rootEntryPropertyStorage); + } + + /** + * Recursively searches the property storages in hierarchy order + * for the appropriate name. + */ + private PropertyStorage findPropertyStorage(String name, + PropertyStorage base) + { + if (base.child == -1) + { + return null; + } + + // Get the child + PropertyStorage child = getPropertyStorage(base.child); + if (child.name.equalsIgnoreCase(name)) + { + return child; + } + + // Find the previous property storages on the same level + PropertyStorage prev = child; + while (prev.previous != -1) + { + prev = getPropertyStorage(prev.previous); + if (prev.name.equalsIgnoreCase(name)) + { + return prev; + } + } + + // Find the next property storages on the same level + PropertyStorage next = child; + while (next.next != -1) + { + next = getPropertyStorage(next.next); + if (next.name.equalsIgnoreCase(name)) + { + return next; + } + } + + return findPropertyStorage(name, child); + } + + /** + * Gets the property set with the specified name + * @param name the property storage name + * @return the property storage record + * @exception BiffException + * @deprecated remove me + */ + private PropertyStorage getPropertyStorage(String name) + throws BiffException + { + // Find the workbook property + Iterator i = propertySets.iterator(); + boolean found = false; + boolean multiple = false; + PropertyStorage ps = null; + while (i.hasNext()) + { + PropertyStorage ps2 = (PropertyStorage) i.next(); + if (ps2.name.equalsIgnoreCase(name)) + { + multiple = found == true ? true : false; + found = true; + ps = ps2; + } + } + + if (multiple) + { + logger.warn("found multiple copies of property set " + name); + } + + if (!found) + { + throw new BiffException(BiffException.streamNotFound); + } + + return ps; + } + + /** + * Gets the property set with the specified name + * @param index the index of the property storage + * @return the property storage record + */ + private PropertyStorage getPropertyStorage(int index) + { + return (PropertyStorage) propertySets.get(index); + } + + /** + * Build up the resultant stream using the big blocks + * + * @param ps the property storage + * @return the big block stream + */ + private byte[] getBigBlockStream(PropertyStorage ps) + { + int numBlocks = ps.size / BIG_BLOCK_SIZE; + if (ps.size % BIG_BLOCK_SIZE != 0) + { + numBlocks++; + } + + byte[] streamData = new byte[numBlocks * BIG_BLOCK_SIZE]; + + int block = ps.startBlock; + + int count = 0; + int pos = 0; + while (block != -2 && count < numBlocks) + { + pos = (block + 1) * BIG_BLOCK_SIZE; + System.arraycopy(data, pos, streamData, + count * BIG_BLOCK_SIZE, BIG_BLOCK_SIZE); + count++; + block = bigBlockChain[block]; + } + + if (block != -2 && count == numBlocks) + { + logger.warn("Property storage size inconsistent with block chain."); + } + + return streamData; + } + + /** + * Build up the resultant stream using the small blocks + * @param ps the property storage + * @return the data + * @exception BiffException + */ + private byte[] getSmallBlockStream(PropertyStorage ps) + throws BiffException + { + /* + PropertyStorage rootps = null; + try + { + rootps = getPropertyStorage(ROOT_ENTRY_NAME); + } + catch (BiffException e) + { + rootps = (PropertyStorage) propertySets.get(0); + } + */ + + byte[] rootdata = readData(rootEntryPropertyStorage.startBlock); + byte[] sbdata = new byte[0]; + + int block = ps.startBlock; + int pos = 0; + + while (block != -2) + { + // grow the array + byte[] olddata = sbdata; + sbdata = new byte[olddata.length + SMALL_BLOCK_SIZE]; + System.arraycopy(olddata, 0, sbdata, 0, olddata.length); + + // Copy in the new data + pos = block * SMALL_BLOCK_SIZE; + System.arraycopy(rootdata, pos, sbdata, + olddata.length, SMALL_BLOCK_SIZE); + block = smallBlockChain[block]; + + if (block == -1) + { + logger.warn("Incorrect terminator for small block stream " + ps.name); + block = -2; // kludge to force the loop termination + } + } + + return sbdata; + } + + /** + * Reads the block chain from the specified block and returns the + * data as a continuous stream of bytes + * + * @param bl the block number + * @return the data + */ + private byte[] readData(int bl) throws BiffException + { + int block = bl; + int pos = 0; + byte[] entry = new byte[0]; + + while (block != -2) + { + // Grow the array + byte[] oldEntry = entry; + entry = new byte[oldEntry.length + BIG_BLOCK_SIZE]; + System.arraycopy(oldEntry, 0, entry, 0, oldEntry.length); + pos = (block + 1) * BIG_BLOCK_SIZE; + System.arraycopy(data, pos, entry, + oldEntry.length, BIG_BLOCK_SIZE); + if (bigBlockChain[block] == block) + { + throw new BiffException(BiffException.corruptFileFormat); + } + block = bigBlockChain[block]; + } + return entry; + } + + /** + * Gets the number of property sets + * @return the number of property sets + */ + public int getNumberOfPropertySets() + { + return propertySets.size(); + } + + /** + * Gets the property set. Invoked when copying worksheets with macros. + * Simply calls the private counterpart + * + * @param ps the property set name + * @return the property set with the given name + */ + public PropertyStorage getPropertySet(int index) + { + return getPropertyStorage(index); + } +} + + + + + + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/CountryRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/CountryRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/CountryRecord.java 17 Aug 2012 14:50:55 -0000 1.1 @@ -0,0 +1,88 @@ +/********************************************************************* +* +* Copyright (C) 2005 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import common.Logger; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; + +/** + * Contains the cell dimensions of this worksheet + */ +public class CountryRecord extends RecordData +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(CountryRecord.class); + + /** + * The user interface language + */ + private int language; + + /** + * The regional settings + */ + private int regionalSettings; + + /** + * Constructs the dimensions from the raw data + * + * @param t the raw data + */ + public CountryRecord(Record t) + { + super(t); + byte[] data = t.getData(); + + language = IntegerHelper.getInt(data[0], data[1]); + regionalSettings = IntegerHelper.getInt(data[2], data[3]); + } + + /** + * Accessor for the language code + * + * @return the language code + */ + public int getLanguageCode() + { + return language; + } + + /** + * Accessor for the regional settings code + * + * @return the regional settings code + */ + public int getRegionalSettingsCode() + { + return regionalSettings; + } + +} + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/DateFormulaRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/DateFormulaRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/DateFormulaRecord.java 17 Aug 2012 14:50:55 -0000 1.1 @@ -0,0 +1,151 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import java.text.NumberFormat; + +import jxl.CellType; +import jxl.DateCell; +import jxl.DateFormulaCell; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; + +/** + * A date formula's last calculated value + */ +class DateFormulaRecord extends DateRecord + implements DateCell, FormulaData, DateFormulaCell +{ + /** + * The formula as an excel string + */ + private String formulaString; + + /** + * A handle to the class needed to access external sheets + */ + private ExternalSheet externalSheet; + + /** + * A handle to the name table + */ + private WorkbookMethods nameTable; + + /** + * The raw data + */ + private byte[] data; + + /** + * Constructs this object from the raw data + * + * @param t the basic number formula record + * @param fr the formatting records + * @param es the external sheet + * @param nt the name table + * @param nf flag indicating whether the 1904 date system is in use + * @param si the sheet + */ + public DateFormulaRecord(NumberFormulaRecord t, FormattingRecords fr, + ExternalSheet es, WorkbookMethods nt, + boolean nf, SheetImpl si) throws FormulaException + { + super(t, t.getXFIndex(), fr, nf, si); + + externalSheet = es; + nameTable = nt; + data = t.getFormulaData(); + } + + /** + * Returns the cell type + * + * @return The cell type + */ + public CellType getType() + { + return CellType.DATE_FORMULA; + } + + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array. Used when copying spreadsheets + * + * @return the raw record data + */ + public byte[] getFormulaData() throws FormulaException + { + if (!getSheet().getWorkbookBof().isBiff8()) + { + throw new FormulaException(FormulaException.BIFF8_SUPPORTED); + } + + // Data is already the formula data, so don't do any more manipulation + return data; + } + + /** + * Gets the formula as an excel string + * + * @return the formula as an excel string + * @exception FormulaException + */ + public String getFormula() throws FormulaException + { + // Note that the standard information was lopped off by the NumberFormula + // record when creating this formula + if (formulaString == null) + { + byte[] tokens = new byte[data.length - 16]; + System.arraycopy(data, 16, tokens, 0, tokens.length); + FormulaParser fp = new FormulaParser + (tokens, this, externalSheet, nameTable, + getSheet().getWorkbook().getSettings()); + fp.parse(); + formulaString = fp.getFormula(); + } + + return formulaString; + } + + /** + * Interface method which returns the value + * + * @return the last calculated value of the formula + */ + public double getValue() + { + return 0; + } + + /** + * Dummy implementation in order to adhere to the NumberCell interface + * + * @return NULL + */ + public NumberFormat getNumberFormat() + { + return null; + } +} Index: 3rdParty_sources/jexcelapi/jxl/read/biff/DateRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/DateRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/DateRecord.java 17 Aug 2012 14:50:55 -0000 1.1 @@ -0,0 +1,351 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + +import common.Assert; +import common.Logger; + +import jxl.CellFeatures; +import jxl.CellType; +import jxl.DateCell; +import jxl.NumberCell; +import jxl.biff.FormattingRecords; +import jxl.format.CellFormat; + +/** + * A date which is stored in the cell + */ +class DateRecord implements DateCell, CellFeaturesAccessor +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(DateRecord.class); + + /** + * The date represented within this cell + */ + private Date date; + /** + * The row number of this cell record + */ + private int row; + /** + * The column number of this cell record + */ + private int column; + + /** + * Indicates whether this is a full date, or merely a time + */ + private boolean time; + + /** + * The format to use when displaying this cell's contents as a string + */ + private DateFormat format; + + /** + * The raw cell format + */ + private CellFormat cellFormat; + + /** + * The index to the XF Record + */ + private int xfIndex; + + /** + * A handle to the formatting records + */ + private FormattingRecords formattingRecords; + + /** + * A handle to the sheet + */ + private SheetImpl sheet; + + /** + * The cell features + */ + private CellFeatures features; + + + /** + * A flag to indicate whether this objects formatting things have + * been initialized + */ + private boolean initialized; + + // The default formats used when returning the date as a string + private static final SimpleDateFormat dateFormat = + new SimpleDateFormat("dd MMM yyyy"); + + private static final SimpleDateFormat timeFormat = + new SimpleDateFormat("HH:mm:ss"); + + // The number of days between 1 Jan 1900 and 1 March 1900. Excel thinks + // the day before this was 29th Feb 1900, but it was 28th Feb 1900. + // I guess the programmers thought nobody would notice that they + // couldn't be bothered to program this dating anomaly properly + private static final int nonLeapDay = 61; + + private static final TimeZone gmtZone = TimeZone.getTimeZone("GMT"); + + // The number of days between 01 Jan 1900 and 01 Jan 1970 - this gives + // the UTC offset + private static final int utcOffsetDays = 25569; + + // The number of days between 01 Jan 1904 and 01 Jan 1970 - this gives + // the UTC offset using the 1904 date system + private static final int utcOffsetDays1904 = 24107; + + // The number of milliseconds in a day + private static final long secondsInADay = 24 * 60 * 60; + private static final long msInASecond = 1000; + private static final long msInADay = secondsInADay * msInASecond; + + + /** + * Constructs this object from the raw data + * + * @param num the numerical representation of this + * @param xfi the java equivalent of the excel date format + * @param fr the formatting records + * @param nf flag indicating whether we are using the 1904 date system + * @param si the sheet + */ + public DateRecord(NumberCell num, + int xfi, FormattingRecords fr, + boolean nf, SheetImpl si) + { + row = num.getRow(); + column = num.getColumn(); + xfIndex = xfi; + formattingRecords = fr; + sheet = si; + initialized = false; + + format = formattingRecords.getDateFormat(xfIndex); + + // This value represents the number of days since 01 Jan 1900 + double numValue = num.getValue(); + + if (Math.abs(numValue) < 1) + { + if (format == null) + { + format = timeFormat; + } + time = true; + } + else + { + if (format == null) + { + format = dateFormat; + } + time = false; + } + + // Work round a bug in excel. Excel seems to think there is a date + // called the 29th Feb, 1900 - but in actual fact this was not a leap year. + // Therefore for values less than 61 in the 1900 date system, + // add one to the numeric value + if (!nf && !time && numValue < nonLeapDay) + { + numValue += 1; + } + + // Get rid of any timezone adjustments - we are not interested + // in automatic adjustments + format.setTimeZone(gmtZone); + + // Convert this to the number of days since 01 Jan 1970 + int offsetDays = nf ? utcOffsetDays1904 : utcOffsetDays; + double utcDays = numValue - offsetDays; + + // Convert this into utc by multiplying by the number of milliseconds + // in a day. Use the round function prior to ms conversion due + // to a rounding feature of Excel (contributed by Jurgen + long utcValue = Math.round(utcDays * secondsInADay) * msInASecond; + + date = new Date(utcValue); + } + + /** + * Interface method which returns the row number of this cell + * + * @return the zero base row number + */ + public final int getRow() + { + return row; + } + + /** + * Interface method which returns the column number of this cell + * + * @return the zero based column number + */ + public final int getColumn() + { + return column; + } + + /** + * Gets the date + * + * @return the date + */ + public Date getDate() + { + return date; + } + + /** + * Gets the cell contents as a string. This method will use the java + * equivalent of the excel formatting string + * + * @return the label + */ + public String getContents() + { + return format.format(date); + } + + /** + * Accessor for the cell type + * + * @return the cell type + */ + public CellType getType() + { + return CellType.DATE; + } + + /** + * Indicates whether the date value contained in this cell refers to a date, + * or merely a time + * + * @return TRUE if the value refers to a time + */ + public boolean isTime() + { + return time; + } + + /** + * Gets the DateFormat used to format the cell. This will normally be + * the format specified in the excel spreadsheet, but in the event of any + * difficulty parsing this, it will revert to the default date/time format. + * + * @return the DateFormat object used to format the date in the original + * excel cell + */ + public DateFormat getDateFormat() + { + Assert.verify(format != null); + + return format; + } + + /** + * Gets the CellFormat object for this cell. Used by the WritableWorkbook + * API + * + * @return the CellFormat used for this cell + */ + public CellFormat getCellFormat() + { + if (!initialized) + { + cellFormat = formattingRecords.getXFRecord(xfIndex); + initialized = true; + } + + return cellFormat; + } + + /** + * Determines whether or not this cell has been hidden + * + * @return TRUE if this cell has been hidden, FALSE otherwise + */ + public boolean isHidden() + { + ColumnInfoRecord cir = sheet.getColumnInfo(column); + + if (cir != null && cir.getWidth() == 0) + { + return true; + } + + RowRecord rr = sheet.getRowInfo(row); + + if (rr != null && (rr.getRowHeight() == 0 || rr.isCollapsed())) + { + return true; + } + + return false; + } + + /** + * Accessor for the sheet + * + * @return the containing sheet + */ + protected final SheetImpl getSheet() + { + return sheet; + } + + /** + * Accessor for the cell features + * + * @return the cell features or NULL if this cell doesn't have any + */ + public CellFeatures getCellFeatures() + { + return features; + } + + /** + * Sets the cell features + * + * @param cf the cell features + */ + public void setCellFeatures(CellFeatures cf) + { + features = cf; + } + +} + + + + + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/DefaultColumnWidthRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/DefaultColumnWidthRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/DefaultColumnWidthRecord.java 17 Aug 2012 14:50:55 -0000 1.1 @@ -0,0 +1,65 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; + +/** + * Contains the default column width for cells in this sheet + */ +class DefaultColumnWidthRecord extends RecordData +{ + /** + * The default columns width, in characters + */ + private int width; + + /** + * Constructs the def col width from the raw data + * + * @param t the raw data + */ + public DefaultColumnWidthRecord(Record t) + { + super(t); + byte[] data = t.getData(); + + width = IntegerHelper.getInt(data[0], data[1]); + } + + + /** + * Accessor for the default width + * + * @return the width + */ + public int getWidth() + { + return width; + } +} + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/DefaultRowHeightRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/DefaultRowHeightRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/DefaultRowHeightRecord.java 17 Aug 2012 14:50:57 -0000 1.1 @@ -0,0 +1,67 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; + +/** + * Contains the default column width for cells in this sheet + */ +class DefaultRowHeightRecord extends RecordData +{ + /** + * The default row height, in 1/20ths of a point + */ + private int height; + + /** + * Constructs the def col width from the raw data + * + * @param t the raw data + */ + public DefaultRowHeightRecord(Record t) + { + super(t); + byte[] data = t.getData(); + + if (data.length > 2) + { + height = IntegerHelper.getInt(data[2], data[3]); + } + } + + /** + * Accessor for the default height + * + * @return the height + */ + public int getHeight() + { + return height; + } +} + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/DimensionRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/DimensionRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/DimensionRecord.java 17 Aug 2012 14:50:58 -0000 1.1 @@ -0,0 +1,135 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import common.Logger; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; + +/** + * Contains the cell dimensions of this worksheet + */ +class DimensionRecord extends RecordData +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(DimensionRecord.class); + + /** + * The number of rows in this sheet + */ + private int numRows; + /** + * The number of columns in this worksheet + */ + private int numCols; + + /** + * Dummy indicators for overloading the constructor + */ + private static class Biff7 {}; + public static Biff7 biff7 = new Biff7(); + + /** + * Constructs the dimensions from the raw data + * + * @param t the raw data + */ + public DimensionRecord(Record t) + { + super(t); + byte[] data = t.getData(); + + // Sometimes, if the spreadsheet is being generated by dodgy VB modules, + // even though the excel format is biff8, the dimension record is + // generated in the old biff 7 format. This horrible if construct + // handles that eventuality + if (data.length == 10) + { + read10ByteData(data); + } + else + { + read14ByteData(data); + } + } + + /** + * Constructs the dimensions from the raw data + * + * @param t the raw data + * @param biff7 an indicator to initialise this record for biff 7 format + */ + public DimensionRecord(Record t, Biff7 biff7) + { + super(t); + byte[] data = t.getData(); + read10ByteData(data); + } + + /** + * Reads in the data for data records of length 10 + * @param data the data to read + */ + private void read10ByteData(byte[] data) + { + numRows = IntegerHelper.getInt(data[2], data[3]); + numCols = IntegerHelper.getInt(data[6], data[7]); + } + + /** + * Reads in the data for data records of length 14 + * @param data the data to read + */ + private void read14ByteData(byte[] data) + { + numRows = IntegerHelper.getInt(data[4], data[5], data[6], data[7]); + numCols = IntegerHelper.getInt(data[10], data[11]); + } + + /** + * Accessor for the number of rows in this sheet + * + * @return the number of rows + */ + public int getNumberOfRows() + { + return numRows; + } + + /** + * Accessor for the number of columns in this sheet + * + * @return the number of columns + */ + public int getNumberOfColumns() + { + return numCols; + } +} + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/ErrorFormulaRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/ErrorFormulaRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/ErrorFormulaRecord.java 17 Aug 2012 14:50:57 -0000 1.1 @@ -0,0 +1,175 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import common.Assert; + +import jxl.CellType; +import jxl.ErrorCell; +import jxl.ErrorFormulaCell; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaErrorCode; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; + +/** + * An error resulting from the calculation of a formula + */ +class ErrorFormulaRecord extends CellValue + implements ErrorCell, FormulaData, ErrorFormulaCell +{ + /** + * The error code of this cell + */ + private int errorCode; + + /** + * A handle to the class needed to access external sheets + */ + private ExternalSheet externalSheet; + + /** + * A handle to the name table + */ + private WorkbookMethods nameTable; + + /** + * The formula as an excel string + */ + private String formulaString; + + /** + * The raw data + */ + private byte[] data; + + /** + * The error code + */ + private FormulaErrorCode error; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param fr the formatting records + * @param es the external sheet + * @param nt the name table + * @param si the sheet + */ + public ErrorFormulaRecord(Record t, FormattingRecords fr, ExternalSheet es, + WorkbookMethods nt, + SheetImpl si) + { + super(t, fr, si); + + externalSheet = es; + nameTable = nt; + data = getRecord().getData(); + + Assert.verify(data[6] == 2); + + errorCode = data[8]; + } + + /** + * Interface method which gets the error code for this cell. If this cell + * does not represent an error, then it returns 0. Always use the + * method isError() to determine this prior to calling this method + * + * @return the error code if this cell contains an error, 0 otherwise + */ + public int getErrorCode() + { + return errorCode; + } + + /** + * Returns the numerical value as a string + * + * @return The numerical value of the formula as a string + */ + public String getContents() + { + if (error == null) + { + error = FormulaErrorCode.getErrorCode(errorCode); + } + + return error != FormulaErrorCode.UNKNOWN ? + error.getDescription() : "ERROR " + errorCode; + } + + /** + * Returns the cell type + * + * @return The cell type + */ + public CellType getType() + { + return CellType.FORMULA_ERROR; + } + + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array + * + * @return the raw record data + */ + public byte[] getFormulaData() throws FormulaException + { + if (!getSheet().getWorkbookBof().isBiff8()) + { + throw new FormulaException(FormulaException.BIFF8_SUPPORTED); + } + + // Lop off the standard information + byte[] d = new byte[data.length - 6]; + System.arraycopy(data, 6, d, 0, data.length - 6); + + return d; + } + + /** + * Gets the formula as an excel string + * + * @return the formula as an excel string + * @exception FormulaException + */ + public String getFormula() throws FormulaException + { + if (formulaString == null) + { + byte[] tokens = new byte[data.length - 22]; + System.arraycopy(data, 22, tokens, 0, tokens.length); + FormulaParser fp = new FormulaParser + (tokens, this, externalSheet, nameTable, + getSheet().getWorkbook().getSettings()); + fp.parse(); + formulaString = fp.getFormula(); + } + + return formulaString; + } +} + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/ErrorRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/ErrorRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/ErrorRecord.java 17 Aug 2012 14:50:56 -0000 1.1 @@ -0,0 +1,85 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import jxl.CellType; +import jxl.ErrorCell; +import jxl.biff.FormattingRecords; + +/** + * A cell containing an error code. This will usually be the result + * of some error during the calculation of a formula + */ +class ErrorRecord extends CellValue implements ErrorCell +{ + /** + * The error code if this cell evaluates to an error, otherwise zer0 + */ + private int errorCode; + + /** + * Constructs this object + * + * @param t the raw data + * @param fr the formatting records + * @param si the sheet + */ + public ErrorRecord(Record t, FormattingRecords fr, SheetImpl si) + { + super(t, fr, si); + + byte[] data = getRecord().getData(); + + errorCode = data[6]; + } + + /** + * Interface method which gets the error code for this cell. If this cell + * does not represent an error, then it returns 0. Always use the + * method isError() to determine this prior to calling this method + * + * @return the error code if this cell contains an error, 0 otherwise + */ + public int getErrorCode() + { + return errorCode; + } + + /** + * Returns the numerical value as a string + * + * @return The numerical value of the formula as a string + */ + public String getContents() + { + return "ERROR " + errorCode; + } + + /** + * Returns the cell type + * + * @return The cell type + */ + public CellType getType() + { + return CellType.ERROR; + } +} + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/ExternalNameRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/ExternalNameRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/ExternalNameRecord.java 17 Aug 2012 14:50:56 -0000 1.1 @@ -0,0 +1,108 @@ +/********************************************************************* +* +* Copyright (C) 2001 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import common.Logger; + +import jxl.WorkbookSettings; +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.StringHelper; + + +/** + * A row record + */ +public class ExternalNameRecord extends RecordData +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(ExternalNameRecord.class); + + /** + * The name + */ + private String name; + + /** + * Add in function flag + */ + private boolean addInFunction; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param ws the workbook settings + */ + ExternalNameRecord(Record t, WorkbookSettings ws) + { + super(t); + + byte[] data = getRecord().getData(); + int options = IntegerHelper.getInt(data[0], data[1]); + + if (options == 0) + { + addInFunction = true; + } + + if (!addInFunction) + { + return; + } + + int length = data[6]; + + boolean unicode = (data[7] != 0); + + if (unicode) + { + name = StringHelper.getUnicodeString(data, length, 8); + } + else + { + name = StringHelper.getString(data, length, 8, ws); + } + } + + /** + * Queries whether this name record refers to an external record + * + * @return TRUE if this name record is an add in function, FALSE otherwise + */ + public boolean isAddInFunction() + { + return addInFunction; + } + + /** + * Gets the name + * + * @return the name + */ + public String getName() + { + return name; + } +} + + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/ExternalSheetRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/ExternalSheetRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/ExternalSheetRecord.java 17 Aug 2012 14:50:58 -0000 1.1 @@ -0,0 +1,191 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import common.Logger; + +import jxl.WorkbookSettings; +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; + +/** + * An Externsheet record, containing the details of externally references + * workbooks + */ +public class ExternalSheetRecord extends RecordData +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(ExternalSheetRecord.class); + + /** + * Dummy indicators for overloading the constructor + */ + private static class Biff7 {}; + public static Biff7 biff7 = new Biff7(); + + /** + * An XTI structure + */ + private static class XTI + { + /** + * the supbook index + */ + int supbookIndex; + /** + * the first tab + */ + int firstTab; + /** + * the last tab + */ + int lastTab; + + /** + * Constructor + * + * @param s the supbook index + * @param f the first tab + * @param l the last tab + */ + XTI(int s, int f, int l) + { + supbookIndex = s; + firstTab = f; + lastTab = l; + } + } + + /** + * The array of XTI structures + */ + private XTI[] xtiArray; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param ws the workbook settings + */ + ExternalSheetRecord(Record t, WorkbookSettings ws) + { + super(t); + byte[] data = getRecord().getData(); + + int numxtis = IntegerHelper.getInt(data[0], data[1]); + + if (data.length < numxtis * 6 + 2) + { + xtiArray = new XTI[0]; + logger.warn("Could not process external sheets. Formulas may " + + "be compromised."); + return; + } + + xtiArray = new XTI[numxtis]; + + int pos = 2; + for (int i = 0; i < numxtis; i++) + { + int s = IntegerHelper.getInt(data[pos], data[pos + 1]); + int f = IntegerHelper.getInt(data[pos + 2], data[pos + 3]); + int l = IntegerHelper.getInt(data[pos + 4], data[pos + 5]); + xtiArray[i] = new XTI(s, f, l); + pos += 6; + } + } + + /** + * Constructs this object from the raw data in biff 7 format. + * Does nothing here + * + * @param t the raw data + * @param settings the workbook settings + * @param dummy dummy override to identify biff7 funcionality + */ + ExternalSheetRecord(Record t, WorkbookSettings settings, Biff7 dummy) + { + super(t); + + logger.warn("External sheet record for Biff 7 not supported"); + } + + /** + * Accessor for the number of external sheet records + * @return the number of XTI records + */ + public int getNumRecords() + { + return xtiArray != null ? xtiArray.length : 0; + } + /** + * Gets the supbook index for the specified external sheet + * + * @param index the index of the supbook record + * @return the supbook index + */ + public int getSupbookIndex(int index) + { + return xtiArray[index].supbookIndex; + } + + /** + * Gets the first tab index for the specified external sheet + * + * @param index the index of the supbook record + * @return the first tab index + */ + public int getFirstTabIndex(int index) + { + return xtiArray[index].firstTab; + } + + /** + * Gets the last tab index for the specified external sheet + * + * @param index the index of the supbook record + * @return the last tab index + */ + public int getLastTabIndex(int index) + { + return xtiArray[index].lastTab; + } + + /** + * Used when copying a workbook to access the raw external sheet data + * + * @return the raw external sheet data + */ + public byte[] getData() + { + return getRecord().getData(); + } +} + + + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/File.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/File.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/File.java 17 Aug 2012 14:50:55 -0000 1.1 @@ -0,0 +1,328 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InterruptedIOException; + +import common.Logger; + +import jxl.WorkbookSettings; +import jxl.biff.BaseCompoundFile; +import jxl.biff.IntegerHelper; +import jxl.biff.Type; + +/** + * File containing the data from the binary stream + */ +public class File +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(File.class); + + /** + * The data from the excel 97 file + */ + private byte[] data; + /** + * The current position within the file + */ + private int filePos; + /** + * The saved pos + */ + private int oldPos; + /** + * The initial file size + */ + private int initialFileSize; + /** + * The amount to increase the growable array by + */ + private int arrayGrowSize; + /** + * A handle to the compound file. This is only preserved when the + * copying of PropertySets is enabled + */ + private CompoundFile compoundFile; + /** + * The workbook settings + */ + private WorkbookSettings workbookSettings; + + /** + * Constructs a file from the input stream + * + * @param is the input stream + * @param ws the workbook settings + * @exception IOException + * @exception BiffException + */ + public File(InputStream is, WorkbookSettings ws) + throws IOException, BiffException + { + // Initialize the file sizing parameters from the settings + workbookSettings = ws; + initialFileSize = workbookSettings.getInitialFileSize(); + arrayGrowSize = workbookSettings.getArrayGrowSize(); + + byte[] d = new byte[initialFileSize]; + int bytesRead = is.read(d); + int pos = bytesRead; + + // Handle thread interruptions, in case the user keeps pressing + // the Submit button from a browser. Thanks to Mike Smith for this + if (Thread.currentThread().isInterrupted()) + { + throw new InterruptedIOException(); + } + + while (bytesRead != -1) + { + if (pos >= d.length) + { + // Grow the array + byte[] newArray = new byte[d.length + arrayGrowSize]; + System.arraycopy(d, 0, newArray, 0, d.length); + d = newArray; + } + bytesRead = is.read(d, pos, d.length - pos); + pos += bytesRead; + + if (Thread.currentThread().isInterrupted()) + { + throw new InterruptedIOException(); + } + } + + bytesRead = pos + 1; + + // Perform file reading checks and throw exceptions as necessary + if (bytesRead == 0) + { + throw new BiffException(BiffException.excelFileNotFound); + } + + CompoundFile cf = new CompoundFile(d, ws); + try + { + data = cf.getStream("workbook"); + } + catch (BiffException e) + { + // this might be in excel 95 format - try again + data = cf.getStream("book"); + } + + if (!workbookSettings.getPropertySetsDisabled() && + (cf.getNumberOfPropertySets() > + BaseCompoundFile.STANDARD_PROPERTY_SETS.length)) + { + compoundFile = cf; + } + + cf = null; + + if (!workbookSettings.getGCDisabled()) + { + System.gc(); + } + + // Uncomment the following lines to send the pure workbook stream + // (ie. a defragged ole stream) to an output file + +// FileOutputStream fos = new FileOutputStream("defraggedxls"); +// fos.write(data); +// fos.close(); + + } + + /** + * Constructs a file from already defragged binary data. Useful for + * displaying subportions of excel streams. This is only used during + * special runs of the "BiffDump" demo program and should not be invoked + * as part of standard JExcelApi parsing + * + * @param d the already parsed data + */ + public File(byte[] d) + { + data = d; + } + + /** + * Returns the next data record and increments the pointer + * + * @return the next data record + */ + Record next() + { + Record r = new Record(data, filePos, this); + return r; + } + + /** + * Peek ahead to the next record, without incrementing the file position + * + * @return the next record + */ + Record peek() + { + int tempPos = filePos; + Record r = new Record(data, filePos, this); + filePos = tempPos; + return r; + } + + /** + * Skips forward the specified number of bytes + * + * @param bytes the number of bytes to skip forward + */ + public void skip(int bytes) + { + filePos += bytes; + } + + /** + * Copies the bytes into a new array and returns it. + * + * @param pos the position to read from + * @param length the number of bytes to read + * @return The bytes read + */ + public byte[] read(int pos, int length) + { + byte[] ret = new byte[length]; + try + { + System.arraycopy(data, pos, ret, 0, length); + } + catch (ArrayIndexOutOfBoundsException e) + { + logger.error("Array index out of bounds at position " + pos + + " record length " + length); + throw e; + } + return ret; + } + + /** + * Gets the position in the stream + * + * @return the position in the stream + */ + public int getPos() + { + return filePos; + } + + /** + * Saves the current position and temporarily sets the position to be the + * new one. The original position may be restored usind the restorePos() + * method. This is used when reading in the cell values of the sheet - an + * addition in 1.6 for memory allocation reasons. + * + * These methods are used by the SheetImpl.readSheet() when it is reading + * in all the cell values + * + * @param p the temporary position + */ + public void setPos(int p) + { + oldPos = filePos; + filePos = p; + } + + /** + * Restores the original position + * + * These methods are used by the SheetImpl.readSheet() when it is reading + * in all the cell values + */ + public void restorePos() + { + filePos = oldPos; + } + + /** + * Moves to the first bof in the file + */ + private void moveToFirstBof() + { + boolean bofFound = false; + while (!bofFound) + { + int code = IntegerHelper.getInt(data[filePos], data[filePos + 1]); + if (code == Type.BOF.value) + { + bofFound = true; + } + else + { + skip(128); + } + } + } + + /** + * "Closes" the biff file + * + * @deprecated As of version 1.6 use workbook.close() instead + */ + public void close() + { + } + + /** + * Clears the contents of the file + */ + public void clear() + { + data = null; + } + + /** + * Determines if the current position exceeds the end of the file + * + * @return TRUE if there is more data left in the array, FALSE otherwise + */ + public boolean hasNext() + { + // Allow four bytes for the record code and its length + return filePos < data.length - 4; + } + + /** + * Accessor for the compound file. The returned value will only be non-null + * if the property sets feature is enabled and the workbook contains + * additional property sets + * + * @return the compound file + */ + CompoundFile getCompoundFile() + { + return compoundFile; + } +} Index: 3rdParty_sources/jexcelapi/jxl/read/biff/FooterRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/FooterRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/FooterRecord.java 17 Aug 2012 14:50:55 -0000 1.1 @@ -0,0 +1,103 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import jxl.WorkbookSettings; +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.StringHelper; + +/** + * A workbook page footer record + */ +public class FooterRecord extends RecordData +{ + /** + * The footer + */ + private String footer; + + /** + * Dummy indicators for overloading the constructor + */ + private static class Biff7 {}; + public static Biff7 biff7 = new Biff7(); + + /** + * Constructs this object from the raw data + * + * @param t the record data + * @param ws the workbook settings + */ + FooterRecord(Record t, WorkbookSettings ws) + { + super(t); + byte[] data = getRecord().getData(); + + if (data.length == 0) + { + return; + } + + int chars = IntegerHelper.getInt(data[0], data[1]); + + boolean unicode = data[2] == 1; + + if (unicode) + { + footer = StringHelper.getUnicodeString(data, chars, 3); + } + else + { + footer = StringHelper.getString(data, chars, 3, ws); + } + } + + /** + * Constructs this object from the raw data + * + * @param t the record data + * @param ws the workbook settings + * @param dummy dummy record to indicate a biff7 document + */ + FooterRecord(Record t, WorkbookSettings ws, Biff7 dummy) + { + super(t); + byte[] data = getRecord().getData(); + + if (data.length == 0) + { + return; + } + + int chars = data[0]; + footer = StringHelper.getString(data, chars, 1, ws); + } + + /** + * Gets the footer string + * + * @return the footer string + */ + String getFooter() + { + return footer; + } +} Index: 3rdParty_sources/jexcelapi/jxl/read/biff/Formula.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/Formula.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/Formula.java 17 Aug 2012 14:50:57 -0000 1.1 @@ -0,0 +1,37 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import jxl.Cell; + +/** + * Interface which is used for copying formulas from a read only + * to a writable spreadsheet + */ +public interface Formula extends Cell +{ + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array + * + * @return the raw record data + */ + public byte[] getFormulaData(); +} Index: 3rdParty_sources/jexcelapi/jxl/read/biff/FormulaRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/FormulaRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/FormulaRecord.java 17 Aug 2012 14:50:56 -0000 1.1 @@ -0,0 +1,278 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import common.Assert; +import common.Logger; + +import jxl.CellType; +import jxl.WorkbookSettings; +import jxl.biff.DoubleHelper; +import jxl.biff.FormattingRecords; +import jxl.biff.IntegerHelper; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; + +/** + * A formula's last calculated value + */ +class FormulaRecord extends CellValue +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(FormulaRecord.class); + + /** + * The "real" formula record - will be either a string a or a number + */ + private CellValue formula; + + /** + * Flag to indicate whether this is a shared formula + */ + private boolean shared; + + /** + * Static class for a dummy override, indicating that the formula + * passed in is not a shared formula + */ + private static class IgnoreSharedFormula {}; + public static final IgnoreSharedFormula ignoreSharedFormula + = new IgnoreSharedFormula(); + + /** + * Constructs this object from the raw data. Creates either a + * NumberFormulaRecord or a StringFormulaRecord depending on whether + * this formula represents a numerical calculation or not + * + * @param t the raw data + * @param excelFile the excel file + * @param fr the formatting records + * @param es the workbook, which contains the external sheet references + * @param nt the name table + * @param si the sheet + * @param ws the workbook settings + */ + public FormulaRecord(Record t, + File excelFile, + FormattingRecords fr, + ExternalSheet es, + WorkbookMethods nt, + SheetImpl si, + WorkbookSettings ws) + { + super(t, fr, si); + + byte[] data = getRecord().getData(); + + shared = false; + + // Check to see if this forms part of a shared formula + int grbit = IntegerHelper.getInt(data[14], data[15]); + if ((grbit & 0x08) != 0) + { + shared = true; + + if (data[6] == 0 && data[12] == -1 && data[13] == -1) + { + // It is a shared string formula + formula = new SharedStringFormulaRecord + (t, excelFile, fr, es, nt, si, ws); + } + else if (data[6] == 3 && data[12] == -1 && data[13] == -1) + { + // We have a string which evaluates to null + formula = new SharedStringFormulaRecord + (t, excelFile, fr, es, nt, si, + SharedStringFormulaRecord.EMPTY_STRING); + } + else if (data[6] == 2 && + data[12] == -1 && + data[13] == -1) + { + // The cell is in error + int errorCode = data[8]; + formula = new SharedErrorFormulaRecord(t, excelFile, errorCode, + fr, es, nt, si); + } + else if (data[6] == 1 && + data[12] == -1 && + data[13] == -1) + { + boolean value = data[8] == 1 ? true : false; + formula = new SharedBooleanFormulaRecord + (t, excelFile, value, fr, es, nt, si); + } + else + { + // It is a numerical formula + double value = DoubleHelper.getIEEEDouble(data, 6); + SharedNumberFormulaRecord snfr = new SharedNumberFormulaRecord + (t, excelFile, value, fr, es, nt, si); + snfr.setNumberFormat(fr.getNumberFormat(getXFIndex())); + formula = snfr; + } + + return; + } + + // microsoft and their goddam magic values determine whether this + // is a string or a number value + if (data[6] == 0 && data[12] == -1 && data[13] == -1) + { + // we have a string + formula = new StringFormulaRecord(t, excelFile, fr, es, nt, si, ws); + } + else if (data[6] == 1 && + data[12] == -1 && + data[13] == -1) + { + // We have a boolean formula + // multiple values. Thanks to Frank for spotting this + formula = new BooleanFormulaRecord(t, fr, es, nt, si); + } + else if (data[6] == 2 && + data[12] == -1 && + data[13] == -1) + { + // The cell is in error + formula = new ErrorFormulaRecord(t, fr, es, nt, si); + } + else if (data[6] == 3 && data[12] == -1 && data[13] == -1) + { + // we have a string which evaluates to null + formula = new StringFormulaRecord(t, fr, es, nt, si); + } + else + { + // it is most assuredly a number + formula = new NumberFormulaRecord(t, fr, es, nt, si); + } + } + + /** + * Constructs this object from the raw data. Creates either a + * NumberFormulaRecord or a StringFormulaRecord depending on whether + * this formula represents a numerical calculation or not + * + * @param t the raw data + * @param excelFile the excel file + * @param fr the formatting records + * @param es the workbook, which contains the external sheet references + * @param nt the name table + * @param i a dummy override to indicate that we don't want to do + * any shared formula processing + * @param si the sheet impl + * @param ws the workbook settings + */ + public FormulaRecord(Record t, + File excelFile, + FormattingRecords fr, + ExternalSheet es, + WorkbookMethods nt, + IgnoreSharedFormula i, + SheetImpl si, + WorkbookSettings ws) + { + super(t, fr, si); + byte[] data = getRecord().getData(); + + shared = false; + + // microsoft and their magic values determine whether this + // is a string or a number value + if (data[6] == 0 && data[12] == -1 && data[13] == -1) + { + // we have a string + formula = new StringFormulaRecord(t, excelFile, fr, es, nt, si, ws); + } + else if (data[6] == 1 && + data[12] == -1 && + data[13] == -1) + { + // We have a boolean formula + // multiple values. Thanks to Frank for spotting this + formula = new BooleanFormulaRecord(t, fr, es, nt, si); + } + else if (data[6] == 2 && + data[12] == -1 && + data[13] == -1) + { + // The cell is in error + formula = new ErrorFormulaRecord(t, fr, es, nt, si); + } + else + { + // it is most assuredly a number + formula = new NumberFormulaRecord(t, fr, es, nt, si); + } + } + + /** + * Returns the numerical value as a string + * + * @return The numerical value of the formula as a string + */ + public String getContents() + { + Assert.verify(false); + return ""; + } + + /** + * Returns the cell type + * + * @return The cell type + */ + public CellType getType() + { + Assert.verify(false); + return CellType.EMPTY; + } + + /** + * Gets the "real" formula + * + * @return the cell value + */ + final CellValue getFormula() + { + return formula; + } + + /** + * Interrogates this formula to determine if it forms part of a shared + * formula + * + * @return TRUE if this is shared formula, FALSE otherwise + */ + final boolean isShared() + { + return shared; + } + +} + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/GuttersRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/GuttersRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/GuttersRecord.java 17 Aug 2012 14:50:57 -0000 1.1 @@ -0,0 +1,61 @@ +/********************************************************************* +* +* Copyright (C) 2007 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; + +/** + * The gutters record + */ +public class GuttersRecord extends RecordData +{ + private int width; + private int height; + private int rowOutlineLevel; + private int columnOutlineLevel; + + /** + * Constructs this object from the raw data + * + * @param r the raw data + */ + public GuttersRecord(Record r) + { + super(r); + + byte[] data = getRecord().getData(); + width = IntegerHelper.getInt(data[0], data[1]); + height = IntegerHelper.getInt(data[2], data[3]); + rowOutlineLevel = IntegerHelper.getInt(data[4], data[5]); + columnOutlineLevel = IntegerHelper.getInt(data[6], data[7]); + } + + int getRowOutlineLevel() + { + return rowOutlineLevel; + } + + int getColumnOutlineLevel() + { + return columnOutlineLevel; + } + +} Index: 3rdParty_sources/jexcelapi/jxl/read/biff/HeaderRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/HeaderRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/HeaderRecord.java 17 Aug 2012 14:50:58 -0000 1.1 @@ -0,0 +1,110 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import common.Logger; + +import jxl.WorkbookSettings; +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.StringHelper; + +/** + * A workbook page header record + */ +public class HeaderRecord extends RecordData +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(HeaderRecord.class); + + /** + * The footer + */ + private String header; + + /** + * Dummy indicators for overloading the constructor + */ + private static class Biff7 {}; + public static Biff7 biff7 = new Biff7(); + + /** + * Constructs this object from the raw data + * + * @param t the record data + * @param ws the workbook settings + */ + HeaderRecord(Record t, WorkbookSettings ws) + { + super(t); + byte[] data = getRecord().getData(); + + if (data.length == 0) + { + return; + } + + int chars = IntegerHelper.getInt(data[0], data[1]); + + boolean unicode = data[2] == 1; + + if (unicode) + { + header = StringHelper.getUnicodeString(data, chars, 3); + } + else + { + header = StringHelper.getString(data, chars, 3, ws); + } + } + + /** + * Constructs this object from the raw data + * + * @param t the record data + * @param ws the workbook settings + * @param dummy dummy record to indicate a biff7 document + */ + HeaderRecord(Record t, WorkbookSettings ws, Biff7 dummy) + { + super(t); + byte[] data = getRecord().getData(); + + if (data.length == 0) + { + return; + } + + int chars = data[0]; + header = StringHelper.getString(data, chars, 1, ws); + } + + /** + * Gets the header string + * + * @return the header string + */ + String getHeader() + { + return header; + } +} Index: 3rdParty_sources/jexcelapi/jxl/read/biff/HorizontalPageBreaksRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/HorizontalPageBreaksRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/HorizontalPageBreaksRecord.java 17 Aug 2012 14:50:58 -0000 1.1 @@ -0,0 +1,108 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import common.Logger; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; + +/** + * Contains the cell dimensions of this worksheet + */ +class HorizontalPageBreaksRecord extends RecordData +{ + /** + * The logger + */ + private final Logger logger = Logger.getLogger + (HorizontalPageBreaksRecord.class); + + /** + * The row page breaks + */ + private int[] rowBreaks; + + /** + * Dummy indicators for overloading the constructor + */ + private static class Biff7 {}; + public static Biff7 biff7 = new Biff7(); + + /** + * Constructs the dimensions from the raw data + * + * @param t the raw data + */ + public HorizontalPageBreaksRecord(Record t) + { + super(t); + + byte[] data = t.getData(); + + int numbreaks = IntegerHelper.getInt(data[0], data[1]); + int pos = 2; + rowBreaks = new int[numbreaks]; + + for (int i = 0; i < numbreaks; i++) + { + rowBreaks[i] = IntegerHelper.getInt(data[pos], data[pos + 1]); + pos += 6; + } + } + + /** + * Constructs the dimensions from the raw data + * + * @param t the raw data + * @param biff7 an indicator to initialise this record for biff 7 format + */ + public HorizontalPageBreaksRecord(Record t, Biff7 biff7) + { + super(t); + + byte[] data = t.getData(); + int numbreaks = IntegerHelper.getInt(data[0], data[1]); + int pos = 2; + rowBreaks = new int[numbreaks]; + for (int i = 0; i < numbreaks; i++) + { + rowBreaks[i] = IntegerHelper.getInt(data[pos], data[pos + 1]); + pos += 2; + } + } + + /** + * Gets the row breaks + * + * @return the row breaks on the current sheet + */ + public int[] getRowBreaks() + { + return rowBreaks; + } +} + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/HyperlinkRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/HyperlinkRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/HyperlinkRecord.java 17 Aug 2012 14:50:56 -0000 1.1 @@ -0,0 +1,417 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; + +import common.Logger; + +import jxl.CellReferenceHelper; +import jxl.Hyperlink; +import jxl.Range; +import jxl.Sheet; +import jxl.WorkbookSettings; +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.SheetRangeImpl; +import jxl.biff.StringHelper; + +/** + * A number record. This is stored as 8 bytes, as opposed to the + * 4 byte RK record + */ +public class HyperlinkRecord extends RecordData implements Hyperlink +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(HyperlinkRecord.class); + + /** + * The first row + */ + private int firstRow; + /** + * The last row + */ + private int lastRow; + /** + * The first column + */ + private int firstColumn; + /** + * The last column + */ + private int lastColumn; + + /** + * The URL referred to by this hyperlink + */ + private URL url; + + /** + * The local file referred to by this hyperlink + */ + private File file; + + /** + * The location in this workbook referred to by this hyperlink + */ + private String location; + + /** + * The range of cells which activate this hyperlink + */ + private SheetRangeImpl range; + + /** + * The type of this hyperlink + */ + private LinkType linkType; + + /** + * The excel type of hyperlink + */ + private static class LinkType {}; + + private static final LinkType urlLink = new LinkType(); + private static final LinkType fileLink = new LinkType(); + private static final LinkType workbookLink = new LinkType(); + private static final LinkType unknown = new LinkType(); + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param s the sheet + * @param ws the workbook settings + */ + HyperlinkRecord(Record t, Sheet s, WorkbookSettings ws) + { + super(t); + + linkType = unknown; + + byte[] data = getRecord().getData(); + + // Build up the range of cells occupied by this hyperlink + firstRow = IntegerHelper.getInt(data[0], data[1]); + lastRow = IntegerHelper.getInt(data[2], data[3]); + firstColumn = IntegerHelper.getInt(data[4], data[5]); + lastColumn = IntegerHelper.getInt(data[6], data[7]); + range = new SheetRangeImpl(s, + firstColumn, firstRow, + lastColumn, lastRow); + + int options = IntegerHelper.getInt(data[28], data[29], data[30], data[31]); + + boolean description = (options & 0x14) != 0; + int startpos = 32; + int descbytes = 0; + if (description) + { + int descchars = IntegerHelper.getInt + (data[startpos], data[startpos + 1], + data[startpos + 2], data[startpos + 3]); + descbytes = descchars * 2 + 4; + } + + startpos += descbytes; + + boolean targetFrame = (options & 0x80) != 0; + int targetbytes = 0; + if (targetFrame) + { + int targetchars = IntegerHelper.getInt + (data[startpos], data[startpos + 1], + data[startpos + 2], data[startpos + 3]); + targetbytes = targetchars * 2 + 4; + } + + startpos += targetbytes; + + // Try and determine the type + if ((options & 0x3) == 0x03) + { + linkType = urlLink; + + // check the guid monicker + if (data[startpos] == 0x03) + { + linkType = fileLink; + } + } + else if ((options & 0x01) != 0) + { + linkType = fileLink; + // check the guid monicker + if (data[startpos] == (byte) 0xe0) + { + linkType = urlLink; + } + } + else if ((options & 0x08) != 0) + { + linkType = workbookLink; + } + + // Try and determine the type + if (linkType == urlLink) + { + String urlString = null; + try + { + startpos += 16; + + // Get the url, ignoring the 0 char at the end + int bytes = IntegerHelper.getInt(data[startpos], + data[startpos + 1], + data[startpos + 2], + data[startpos + 3]); + + urlString = StringHelper.getUnicodeString(data, bytes / 2 - 1, + startpos + 4); + url = new URL(urlString); + } + catch (MalformedURLException e) + { + logger.warn("URL " + urlString + " is malformed. Trying a file"); + try + { + linkType = fileLink; + file = new File(urlString); + } + catch (Exception e3) + { + logger.warn("Cannot set to file. Setting a default URL"); + + // Set a default URL + try + { + linkType = urlLink; + url = new URL("http://www.andykhan.com/jexcelapi/index.html"); + } + catch (MalformedURLException e2) + { + // fail silently + } + } + } + catch (Throwable e) + { + StringBuffer sb1 = new StringBuffer(); + StringBuffer sb2 = new StringBuffer(); + CellReferenceHelper.getCellReference(firstColumn, firstRow, sb1); + CellReferenceHelper.getCellReference(lastColumn, lastRow, sb2); + sb1.insert(0, "Exception when parsing URL "); + sb1.append('\"').append(sb2.toString()).append("\". Using default."); + logger.warn(sb1, e); + + // Set a default URL + try + { + url = new URL("http://www.andykhan.com/jexcelapi/index.html"); + } + catch (MalformedURLException e2) + { + // fail silently + } + } + } + else if (linkType == fileLink) + { + try + { + startpos += 16; + + // Get the name of the local file, ignoring the zero character at the + // end + int upLevelCount = IntegerHelper.getInt(data[startpos], + data[startpos + 1]); + int chars = IntegerHelper.getInt(data[startpos + 2], + data[startpos + 3], + data[startpos + 4], + data[startpos + 5]); + String fileName = StringHelper.getString(data, chars - 1, + startpos + 6, ws); + + StringBuffer sb = new StringBuffer(); + + for (int i = 0; i < upLevelCount; i++) + { + sb.append("..\\"); + } + + sb.append(fileName); + + file = new File(sb.toString()); + } + catch (Throwable e) + { + e.printStackTrace(); + logger.warn("Exception when parsing file " + + e.getClass().getName() + "."); + file = new File("."); + } + } + else if (linkType == workbookLink) + { + int chars = IntegerHelper.getInt(data[32], data[33], data[34], data[35]); + location = StringHelper.getUnicodeString(data, chars - 1, 36); + } + else + { + // give up + logger.warn("Cannot determine link type"); + return; + } + } + + /** + * Determines whether this is a hyperlink to a file + * + * @return TRUE if this is a hyperlink to a file, FALSE otherwise + */ + public boolean isFile() + { + return linkType == fileLink; + } + + /** + * Determines whether this is a hyperlink to a web resource + * + * @return TRUE if this is a URL + */ + public boolean isURL() + { + return linkType == urlLink; + } + + /** + * Determines whether this is a hyperlink to a location in this workbook + * + * @return TRUE if this is a link to an internal location + */ + public boolean isLocation() + { + return linkType == workbookLink; + } + + /** + * Returns the row number of the top left cell + * + * @return the row number of this cell + */ + public int getRow() + { + return firstRow; + } + + /** + * Returns the column number of the top left cell + * + * @return the column number of this cell + */ + public int getColumn() + { + return firstColumn; + } + + /** + * Returns the row number of the bottom right cell + * + * @return the row number of this cell + */ + public int getLastRow() + { + return lastRow; + } + + /** + * Returns the column number of the bottom right cell + * + * @return the column number of this cell + */ + public int getLastColumn() + { + return lastColumn; + } + + /** + * Gets the URL referenced by this Hyperlink + * + * @return the URL, or NULL if this hyperlink is not a URL + */ + public URL getURL() + { + return url; + } + + /** + * Returns the local file eferenced by this Hyperlink + * + * @return the file, or NULL if this hyperlink is not a file + */ + public File getFile() + { + return file; + } + + /** + * Exposes the base class method. This is used when copying hyperlinks + * + * @return the Record data + */ + public Record getRecord() + { + return super.getRecord(); + } + + /** + * Gets the range of cells which activate this hyperlink + * The get sheet index methods will all return -1, because the + * cells will all be present on the same sheet + * + * @return the range of cells which activate the hyperlink + */ + public Range getRange() + { + return range; + } + + /** + * Gets the location referenced by this hyperlink + * + * @return the location + */ + public String getLocation() + { + return location; + } +} + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/LabelRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/LabelRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/LabelRecord.java 17 Aug 2012 14:50:56 -0000 1.1 @@ -0,0 +1,123 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import jxl.CellType; +import jxl.LabelCell; +import jxl.WorkbookSettings; +import jxl.biff.FormattingRecords; +import jxl.biff.IntegerHelper; +import jxl.biff.StringHelper; + +/** + * A label which is stored in the cell + */ +class LabelRecord extends CellValue implements LabelCell +{ + /** + * The length of the label in characters + */ + private int length; + + /** + * The label + */ + private String string; + + /** + * Dummy indicators for overloading the constructor + */ + private static class Biff7 {}; + public static Biff7 biff7 = new Biff7(); + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param fr the formatting records + * @param si the sheet + * @param ws the workbook settings + */ + public LabelRecord(Record t, FormattingRecords fr, + SheetImpl si, WorkbookSettings ws) + { + super(t, fr, si); + byte[] data = getRecord().getData(); + length = IntegerHelper.getInt(data[6], data[7]); + + if (data[8] == 0x0) + { + string = StringHelper.getString(data, length, 9, ws); + } + else + { + string = StringHelper.getUnicodeString(data, length, 9); + } + } + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param fr the formatting records + * @param si the sheet + * @param ws the workbook settings + * @param dummy dummy overload to indicate a biff 7 workbook + */ + public LabelRecord(Record t, FormattingRecords fr, SheetImpl si, + WorkbookSettings ws, Biff7 dummy) + { + super(t, fr, si); + byte[] data = getRecord().getData(); + length = IntegerHelper.getInt(data[6], data[7]); + + string = StringHelper.getString(data, length, 8, ws); + } + + /** + * Gets the label + * + * @return the label + */ + public String getString() + { + return string; + } + + /** + * Gets the cell contents as a string + * + * @return the label + */ + public String getContents() + { + return string; + } + + /** + * Accessor for the cell type + * + * @return the cell type + */ + public CellType getType() + { + return CellType.LABEL; + } +} Index: 3rdParty_sources/jexcelapi/jxl/read/biff/LabelSSTRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/LabelSSTRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/LabelSSTRecord.java 17 Aug 2012 14:50:57 -0000 1.1 @@ -0,0 +1,88 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import jxl.CellType; +import jxl.LabelCell; +import jxl.biff.FormattingRecords; +import jxl.biff.IntegerHelper; + +/** + * A label which is stored in the shared string table + */ +class LabelSSTRecord extends CellValue implements LabelCell +{ + /** + * The index into the shared string table + */ + private int index; + /** + * The label + */ + private String string; + + /** + * Constructor. Retrieves the index from the raw data and looks it up + * in the shared string table + * + * @param stringTable the shared string table + * @param t the raw data + * @param fr the formatting records + * @param si the sheet + */ + public LabelSSTRecord(Record t, SSTRecord stringTable, FormattingRecords fr, + SheetImpl si) + { + super(t, fr, si); + byte[] data = getRecord().getData(); + index = IntegerHelper.getInt(data[6], data[7], data[8], data[9]); + string = stringTable.getString(index); + } + + /** + * Gets the label + * + * @return the label + */ + public String getString() + { + return string; + } + + /** + * Gets this cell's contents as a string + * + * @return the label + */ + public String getContents() + { + return string; + } + + /** + * Returns the cell type + * + * @return the cell type + */ + public CellType getType() + { + return CellType.LABEL; + } +} Index: 3rdParty_sources/jexcelapi/jxl/read/biff/LeftMarginRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/LeftMarginRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/LeftMarginRecord.java 17 Aug 2012 14:50:55 -0000 1.1 @@ -0,0 +1,38 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.Type; + +/** + * Record for the left margin settings + */ +class LeftMarginRecord extends MarginRecord +{ + /** + * Constructor + * + * @param r the raw record + */ + LeftMarginRecord(Record r) + { + super(Type.LEFTMARGIN, r); + } +} Index: 3rdParty_sources/jexcelapi/jxl/read/biff/MarginRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/MarginRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/MarginRecord.java 17 Aug 2012 14:50:56 -0000 1.1 @@ -0,0 +1,61 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.DoubleHelper; +import jxl.biff.RecordData; +import jxl.biff.Type; + +/** + * Abstract class containing the margin value for top,left,right and bottom + * margins + */ +abstract class MarginRecord extends RecordData +{ + /** + * The size of the margin + */ + private double margin; + + /** + * Constructs this record from the raw data + * + * @param t the type + * @param r the record + */ + protected MarginRecord(Type t, Record r) + { + super(t); + + byte[] data = r.getData(); + + margin = DoubleHelper.getIEEEDouble(data, 0); + } + + /** + * Accessor for the margin + * + * @return the margin + */ + double getMargin() + { + return margin; + } +} Index: 3rdParty_sources/jexcelapi/jxl/read/biff/MergedCellsRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/MergedCellsRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/MergedCellsRecord.java 17 Aug 2012 14:50:56 -0000 1.1 @@ -0,0 +1,90 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import jxl.Range; +import jxl.Sheet; +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.SheetRangeImpl; + +/** + * A merged cells record for a given sheet + */ +public class MergedCellsRecord extends RecordData +{ + /** + * The ranges of the cells merged on this sheet + */ + private Range[] ranges; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param s the sheet + */ + MergedCellsRecord(Record t, Sheet s) + { + super(t); + + byte[] data = getRecord().getData(); + + int numRanges = IntegerHelper.getInt(data[0], data[1]); + + ranges = new Range[numRanges]; + + int pos = 2; + int firstRow = 0; + int lastRow = 0; + int firstCol = 0; + int lastCol = 0; + + for (int i = 0; i < numRanges; i++) + { + firstRow = IntegerHelper.getInt(data[pos], data[pos + 1]); + lastRow = IntegerHelper.getInt(data[pos + 2], data[pos + 3]); + firstCol = IntegerHelper.getInt(data[pos + 4], data[pos + 5]); + lastCol = IntegerHelper.getInt(data[pos + 6], data[pos + 7]); + + ranges[i] = new SheetRangeImpl(s, firstCol, firstRow, + lastCol, lastRow); + + pos += 8; + } + } + + /** + * Gets the ranges which have been merged in this sheet + * + * @return the ranges of cells which have been merged + */ + public Range[] getRanges() + { + return ranges; + } +} + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/MulBlankCell.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/MulBlankCell.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/MulBlankCell.java 17 Aug 2012 14:50:56 -0000 1.1 @@ -0,0 +1,207 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import common.Logger; + +import jxl.Cell; +import jxl.CellFeatures; +import jxl.CellType; +import jxl.biff.FormattingRecords; +import jxl.format.CellFormat; + +/** + * A blank cell value, initialized indirectly from a multiple biff record + * rather than directly from the binary data + */ +class MulBlankCell implements Cell, CellFeaturesAccessor +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(MulBlankCell.class); + + /** + * The row containing this blank + */ + private int row; + /** + * The column containing this blank + */ + private int column; + /** + * The raw cell format + */ + private CellFormat cellFormat; + + /** + * The index to the XF Record + */ + private int xfIndex; + + /** + * A handle to the formatting records + */ + private FormattingRecords formattingRecords; + + /** + * A flag to indicate whether this object's formatting things have + * been initialized + */ + private boolean initialized; + + /** + * A handle to the sheet + */ + private SheetImpl sheet; + + /** + * The cell features + */ + private CellFeatures features; + + + /** + * Constructs this cell + * + * @param r the zero based row + * @param c the zero base column + * @param xfi the xf index + * @param fr the formatting records + * @param si the sheet + */ + public MulBlankCell(int r, int c, + int xfi, + FormattingRecords fr, + SheetImpl si) + { + row = r; + column = c; + xfIndex = xfi; + formattingRecords = fr; + sheet = si; + initialized = false; + } + + /** + * Accessor for the row + * + * @return the zero based row + */ + public final int getRow() + { + return row; + } + + /** + * Accessor for the column + * + * @return the zero based column + */ + public final int getColumn() + { + return column; + } + + /** + * Accessor for the contents as a string + * + * @return the value as a string + */ + public String getContents() + { + return ""; + } + + /** + * Accessor for the cell type + * + * @return the cell type + */ + public CellType getType() + { + return CellType.EMPTY; + } + + /** + * Gets the cell format for this cell + * + * @return the cell format for these cells + */ + public CellFormat getCellFormat() + { + if (!initialized) + { + cellFormat = formattingRecords.getXFRecord(xfIndex); + initialized = true; + } + + return cellFormat; + } + + /** + * Determines whether or not this cell has been hidden + * + * @return TRUE if this cell has been hidden, FALSE otherwise + */ + public boolean isHidden() + { + ColumnInfoRecord cir = sheet.getColumnInfo(column); + + if (cir != null && cir.getWidth() == 0) + { + return true; + } + + RowRecord rr = sheet.getRowInfo(row); + + if (rr != null && (rr.getRowHeight() == 0 || rr.isCollapsed())) + { + return true; + } + + return false; + } + + /** + * Accessor for the cell features + * + * @return the cell features or NULL if this cell doesn't have any + */ + public CellFeatures getCellFeatures() + { + return features; + } + + /** + * Sets the cell features during the reading process + * + * @param cf the cell features + */ + public void setCellFeatures(CellFeatures cf) + { + if (features != null) + { + logger.warn("current cell features not null - overwriting"); + } + + features = cf; + } +} Index: 3rdParty_sources/jexcelapi/jxl/read/biff/MulBlankRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/MulBlankRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/MulBlankRecord.java 17 Aug 2012 14:50:55 -0000 1.1 @@ -0,0 +1,135 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import common.Logger; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; + +/** + * Contains an array of Blank, formatted cells + */ +class MulBlankRecord extends RecordData +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(MulBlankRecord.class); + + /** + * The row containing these numbers + */ + private int row; + /** + * The first column these rk number occur on + */ + private int colFirst; + /** + * The last column these blank numbers occur on + */ + private int colLast; + /** + * The number of blank numbers contained in this record + */ + private int numblanks; + /** + * The array of xf indices + */ + private int[] xfIndices; + + /** + * Constructs the blank records from the raw data + * + * @param t the raw data + */ + public MulBlankRecord(Record t) + { + super(t); + byte[] data = getRecord().getData(); + int length = getRecord().getLength(); + row = IntegerHelper.getInt(data[0], data[1]); + colFirst = IntegerHelper.getInt(data[2], data[3]); + colLast = IntegerHelper.getInt(data[length - 2], data[length - 1]); + numblanks = colLast - colFirst + 1; + xfIndices = new int[numblanks]; + + readBlanks(data); + } + + /** + * Reads the blanks from the raw data + * + * @param data the raw data + */ + private void readBlanks(byte[] data) + { + int pos = 4; + for (int i = 0; i < numblanks; i++) + { + xfIndices[i] = IntegerHelper.getInt(data[pos], data[pos + 1]); + pos += 2; + } + } + + /** + * Accessor for the row + * + * @return the row of containing these blank numbers + */ + public int getRow() + { + return row; + } + + /** + * The first column containing the blank numbers + * + * @return the first column + */ + public int getFirstColumn() + { + return colFirst; + } + + /** + * Accessor for the number of blank values + * + * @return the number of blank values + */ + public int getNumberOfColumns() + { + return numblanks; + } + + /** + * Return a specific formatting index + * @param index the cell index in the group + * @return the formatting index + */ + public int getXFIndex(int index) + { + return xfIndices[index]; + } +} + + + + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/MulRKRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/MulRKRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/MulRKRecord.java 17 Aug 2012 14:50:57 -0000 1.1 @@ -0,0 +1,156 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import common.Logger; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; + +/** + * Contains an array of RK numbers + */ +class MulRKRecord extends RecordData +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(MulRKRecord.class); + + /** + * The row containing these numbers + */ + private int row; + /** + * The first column these rk number occur on + */ + private int colFirst; + /** + * The last column these rk numbers occur on + */ + private int colLast; + /** + * The number of rk numbers contained in this record + */ + private int numrks; + /** + * The array of rk numbers + */ + private int[] rknumbers; + /** + * The array of xf indices + */ + private int[] xfIndices; + + /** + * Constructs the rk numbers from the raw data + * + * @param t the raw data + */ + public MulRKRecord(Record t) + { + super(t); + byte[] data = getRecord().getData(); + int length = getRecord().getLength(); + row = IntegerHelper.getInt(data[0], data[1]); + colFirst = IntegerHelper.getInt(data[2], data[3]); + colLast = IntegerHelper.getInt(data[length - 2], data[length - 1]); + numrks = colLast - colFirst + 1; + rknumbers = new int[numrks]; + xfIndices = new int[numrks]; + + readRks(data); + } + + /** + * Reads the rks from the raw data + * + * @param data the raw data + */ + private void readRks(byte[] data) + { + int pos = 4; + int rk; + for (int i = 0; i < numrks; i++) + { + xfIndices[i] = IntegerHelper.getInt(data[pos], data[pos + 1]); + rk = IntegerHelper.getInt + (data[pos + 2], data[pos + 3], data[pos + 4], data[pos + 5]); + rknumbers[i] = rk; + pos += 6; + } + } + + /** + * Accessor for the row + * + * @return the row of containing these rk numbers + */ + public int getRow() + { + return row; + } + + /** + * The first column containing the rk numbers + * + * @return the first column + */ + public int getFirstColumn() + { + return colFirst; + } + + /** + * Accessor for the number of rk values + * + * @return the number of rk values + */ + public int getNumberOfColumns() + { + return numrks; + } + + /** + * Returns a specific rk number + * + * @param index the rk number to return + * @return the rk number in bits + */ + public int getRKNumber(int index) + { + return rknumbers[index]; + } + + /** + * Return a specific formatting index + * + * @param index the index of the cell in this group + * @return the xf index + */ + public int getXFIndex(int index) + { + return xfIndices[index]; + } +} + + + + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/NameRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/NameRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/NameRecord.java 17 Aug 2012 14:50:57 -0000 1.1 @@ -0,0 +1,580 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import java.util.ArrayList; + +import common.Assert; +import common.Logger; + +import jxl.WorkbookSettings; +import jxl.biff.BuiltInName; +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.StringHelper; + +/** + * Holds an excel name record, and the details of the cells/ranges it refers + * to + */ +public class NameRecord extends RecordData +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(NameRecord.class); + + /** + * The name + */ + private String name; + + /** + * The built in name + */ + private BuiltInName builtInName; + + /** + * The 0-based index in the name table + */ + private int index; + + /** + * The 0-based index sheet reference for a record name + * 0 is for a global reference + */ + private int sheetRef = 0; + + /** + * Indicates whether this is a biff8 name record. Used during copying + */ + private boolean isbiff8; + + /** + * Dummy indicators for overloading the constructor + */ + private static class Biff7 {}; + public static Biff7 biff7 = new Biff7(); + + // Constants which refer to the name type + private static final int commandMacro = 0xc; + private static final int builtIn = 0x20; + + // Constants which refer to the parse tokens after the string + private static final int cellReference = 0x3a; + private static final int areaReference = 0x3b; + private static final int subExpression = 0x29; + private static final int union = 0x10; + + /** + * A nested class to hold range information + */ + public class NameRange + { + /** + * The first column + */ + private int columnFirst; + + /** + * The first row + */ + private int rowFirst; + + /** + * The last column + */ + private int columnLast; + + /** + * The last row + */ + private int rowLast; + + /** + * The first sheet + */ + private int externalSheet; + + /** + * Constructor + * + * @param s1 the sheet + * @param c1 the first column + * @param r1 the first row + * @param c2 the last column + * @param r2 the last row + */ + NameRange(int s1, int c1, int r1, int c2, int r2) + { + columnFirst = c1; + rowFirst = r1; + columnLast = c2; + rowLast = r2; + externalSheet = s1; + } + + /** + * Accessor for the first column + * + * @return the index of the first column + */ + public int getFirstColumn() + { + return columnFirst; + } + + /** + * Accessor for the first row + * + * @return the index of the first row + */ + public int getFirstRow() + { + return rowFirst; + } + + /** + * Accessor for the last column + * + * @return the index of the last column + */ + public int getLastColumn() + { + return columnLast; + } + + /** + * Accessor for the last row + * + * @return the index of the last row + */ + public int getLastRow() + { + return rowLast; + } + + /** + * Accessor for the first sheet + * + * @return the index of the external sheet + */ + public int getExternalSheet() + { + return externalSheet; + } + } + + /** + * The ranges referenced by this name + */ + private ArrayList ranges; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param ws the workbook settings + * @param ind the index in the name table + */ + NameRecord(Record t, WorkbookSettings ws, int ind) + { + super(t); + index = ind; + isbiff8 = true; + + try + { + ranges = new ArrayList(); + + byte[] data = getRecord().getData(); + int option = IntegerHelper.getInt(data[0], data[1]); + int length = data[3]; + sheetRef = IntegerHelper.getInt(data[8],data[9]); + + if ((option & builtIn) != 0) + { + builtInName = BuiltInName.getBuiltInName(data[15]); + } + else + { + name = StringHelper.getString(data, length, 15, ws); + } + + if ((option & commandMacro) != 0) + { + // This is a command macro, so it has no cell references + return; + } + + int pos = length + 15; + + if (data[pos] == cellReference) + { + int sheet = IntegerHelper.getInt(data[pos + 1], data[pos + 2]); + int row = IntegerHelper.getInt(data[pos + 3], data[pos + 4]); + int columnMask = IntegerHelper.getInt(data[pos + 5], data[pos + 6]); + int column = columnMask & 0xff; + + // Check that we are not dealing with offsets + Assert.verify((columnMask & 0xc0000) == 0); + + NameRange r = new NameRange(sheet, column, row, column, row); + ranges.add(r); + } + else if (data[pos] == areaReference) + { + int sheet1 = 0; + int r1 = 0; + int columnMask = 0; + int c1 = 0; + int r2 = 0; + int c2 = 0; + NameRange range = null; + + while (pos < data.length) + { + sheet1 = IntegerHelper.getInt(data[pos + 1], data[pos + 2]); + r1 = IntegerHelper.getInt(data[pos + 3], data[pos + 4]); + r2 = IntegerHelper.getInt(data[pos + 5], data[pos + 6]); + + columnMask = IntegerHelper.getInt(data[pos + 7], data[pos + 8]); + c1 = columnMask & 0xff; + + // Check that we are not dealing with offsets + Assert.verify((columnMask & 0xc0000) == 0); + + columnMask = IntegerHelper.getInt(data[pos + 9], data[pos + 10]); + c2 = columnMask & 0xff; + + // Check that we are not dealing with offsets + Assert.verify((columnMask & 0xc0000) == 0); + + range = new NameRange(sheet1, c1, r1, c2, r2); + ranges.add(range); + + pos += 11; + } + } + else if (data[pos] == subExpression) + { + int sheet1 = 0; + int r1 = 0; + int columnMask = 0; + int c1 = 0; + int r2 = 0; + int c2 = 0; + NameRange range = null; + + // Consume unnecessary parsed tokens + if (pos < data.length && + data[pos] != cellReference && + data[pos] != areaReference) + { + if (data[pos] == subExpression) + { + pos += 3; + } + else if (data[pos] == union) + { + pos += 1; + } + } + + while (pos < data.length) + { + sheet1 = IntegerHelper.getInt(data[pos + 1], data[pos + 2]); + r1 = IntegerHelper.getInt(data[pos + 3], data[pos + 4]); + r2 = IntegerHelper.getInt(data[pos + 5], data[pos + 6]); + + columnMask = IntegerHelper.getInt(data[pos + 7], data[pos + 8]); + c1 = columnMask & 0xff; + + // Check that we are not dealing with offsets + Assert.verify((columnMask & 0xc0000) == 0); + + columnMask = IntegerHelper.getInt(data[pos + 9], data[pos + 10]); + c2 = columnMask & 0xff; + + // Check that we are not dealing with offsets + Assert.verify((columnMask & 0xc0000) == 0); + + range = new NameRange(sheet1, c1, r1, c2, r2); + ranges.add(range); + + pos += 11; + + // Consume unnecessary parsed tokens + if (pos < data.length && + data[pos] != cellReference && + data[pos] != areaReference) + { + if (data[pos] == subExpression) + { + pos += 3; + } + else if (data[pos] == union) + { + pos += 1; + } + } + } + } + else + { + String n = name != null ? name : builtInName.getName(); + logger.warn("Cannot read name ranges for " + n + + " - setting to empty"); + NameRange range = new NameRange(0,0,0,0,0); + ranges.add(range); + } + } + catch (Throwable t1) + { + // Generate a warning + // Names are really a nice to have, and we don't want to halt the + // reading process for functionality that probably won't be used + logger.warn("Cannot read name"); + name = "ERROR"; + } + } + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param ws the workbook settings + * @param ind the index in the name table + * @param dummy dummy parameter to indicate a biff7 workbook + */ + NameRecord(Record t, WorkbookSettings ws, int ind, Biff7 dummy) + { + super(t); + index = ind; + isbiff8 = false; + + try + { + ranges = new ArrayList(); + byte[] data = getRecord().getData(); + int length = data[3]; + sheetRef = IntegerHelper.getInt(data[8], data[9]); + name = StringHelper.getString(data, length, 14, ws); + + int pos = length + 14; + + if (pos >= data.length) + { + // There appears to be nothing after the name, so return + return; + } + + if (data[pos] == cellReference) + { + int sheet = IntegerHelper.getInt(data[pos + 11], data[pos + 12]); + int row = IntegerHelper.getInt(data[pos + 15], data[pos + 16]); + int column = data[pos + 17]; + + NameRange r = new NameRange(sheet, column, row, column, row); + ranges.add(r); + } + else if (data[pos] == areaReference) + { + int sheet1 = 0; + int r1 = 0; + int c1 = 0; + int r2 = 0; + int c2 = 0; + NameRange range = null; + + while (pos < data.length) + { + sheet1 = IntegerHelper.getInt(data[pos + 11], data[pos + 12]); + r1 = IntegerHelper.getInt(data[pos + 15], data[pos + 16]); + r2 = IntegerHelper.getInt(data[pos + 17], data[pos + 18]); + + c1 = data[pos + 19]; + c2 = data[pos + 20]; + + range = new NameRange(sheet1, c1, r1, c2, r2); + ranges.add(range); + + pos += 21; + } + } + else if (data[pos] == subExpression) + { + int sheet1 = 0; + int sheet2 = 0; + int r1 = 0; + int c1 = 0; + int r2 = 0; + int c2 = 0; + NameRange range = null; + + // Consume unnecessary parsed tokens + if (pos < data.length && + data[pos] != cellReference && + data[pos] != areaReference) + { + if (data[pos] == subExpression) + { + pos += 3; + } + else if (data[pos] == union) + { + pos += 1; + } + } + + while (pos < data.length) + { + sheet1 = IntegerHelper.getInt(data[pos + 11], data[pos + 12]); + r1 = IntegerHelper.getInt(data[pos + 15], data[pos + 16]); + r2 = IntegerHelper.getInt(data[pos + 17], data[pos + 18]); + + c1 = data[pos + 19]; + c2 = data[pos + 20]; + + range = new NameRange(sheet1, c1, r1, c2, r2); + ranges.add(range); + + pos += 21; + + // Consume unnecessary parsed tokens + if (pos < data.length && + data[pos] != cellReference && + data[pos] != areaReference) + { + if (data[pos] == subExpression) + { + pos += 3; + } + else if (data[pos] == union) + { + pos += 1; + } + } + } + } + } + catch (Throwable t1) + { + // Generate a warning + // Names are really a nice to have, and we don't want to halt the + // reading process for functionality that probably won't be used + logger.warn("Cannot read name."); + name = "ERROR"; + } + } + + /** + * Gets the name + * + * @return the strings + */ + public String getName() + { + return name; + } + + /** + * Gets the built in name + * + * @return the built in name + */ + public BuiltInName getBuiltInName() + { + return builtInName; + } + + /** + * Gets the array of ranges for this name. This method is public as it is + * used from the writable side when copying ranges + * + * @return the ranges + */ + public NameRange[] getRanges() + { + NameRange[] nr = new NameRange[ranges.size()]; + return (NameRange[]) ranges.toArray(nr); + } + + /** + * Accessor for the index into the name table + * + * @return the 0-based index into the name table + */ + int getIndex() + { + return index; + } + + /** + * The 0-based index sheet reference for a record name + * 0 is for a global reference + * + * @return the sheet reference for name formula + */ + public int getSheetRef() + { + return sheetRef; + } + + /** + * Set the index sheet reference for a record name + * 0 is for a global reference + */ + public void setSheetRef(int i) + { + sheetRef = i; + } + + /** + * Called when copying a sheet. Just returns the raw data + * + * @return the raw data + */ + public byte[] getData() + { + return getRecord().getData(); + } + + /** + * Called when copying to determine whether this is a biff8 name + * + * @return TRUE if this is a biff8 name record, FALSE otherwise + */ + public boolean isBiff8() + { + return isbiff8; + } + + /** + * Queries whether this is a global name or not + * + * @return TRUE if this is a global name, FALSE otherwise + */ + public boolean isGlobal() + { + return sheetRef == 0; + } +} + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/NineteenFourRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/NineteenFourRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/NineteenFourRecord.java 17 Aug 2012 14:50:55 -0000 1.1 @@ -0,0 +1,64 @@ +/********************************************************************* +* +* Copyright (C) 2001 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.RecordData; + +/** + * Identifies the date system as the 1904 system or not + */ +class NineteenFourRecord extends RecordData +{ + /** + * The base year for dates + */ + private boolean nineteenFour; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + */ + public NineteenFourRecord(Record t) + { + super(t); + + byte[] data = getRecord().getData(); + + nineteenFour = data[0] == 1 ? true : false; + + } + + /** + * Accessor to see whether this spreadsheets dates are based around + * 1904 + * + * @return true if this workbooks dates are based around the 1904 + * date system + */ + public boolean is1904() + { + return nineteenFour; + } + + +} + + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/NumberFormulaRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/NumberFormulaRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/NumberFormulaRecord.java 17 Aug 2012 14:50:56 -0000 1.1 @@ -0,0 +1,196 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import java.text.DecimalFormat; +import java.text.NumberFormat; + +import common.Logger; + +import jxl.CellType; +import jxl.NumberCell; +import jxl.NumberFormulaCell; +import jxl.biff.DoubleHelper; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; + +/** + * A formula's last calculated value + */ +class NumberFormulaRecord extends CellValue + implements NumberCell, FormulaData, NumberFormulaCell +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(NumberFormulaRecord.class); + + /** + * The last calculated value of the formula + */ + private double value; + + /** + * The number format + */ + private NumberFormat format; + + /** + * The string format for the double value + */ + private static final DecimalFormat defaultFormat = + new DecimalFormat("#.###"); + + /** + * The formula as an excel string + */ + private String formulaString; + + /** + * A handle to the class needed to access external sheets + */ + private ExternalSheet externalSheet; + + /** + * A handle to the name table + */ + private WorkbookMethods nameTable; + + /** + * The raw data + */ + private byte[] data; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param fr the formatting record + * @param es the external sheet + * @param nt the name table + * @param si the sheet + */ + public NumberFormulaRecord(Record t, FormattingRecords fr, + ExternalSheet es, WorkbookMethods nt, + SheetImpl si) + { + super(t, fr, si); + + externalSheet = es; + nameTable = nt; + data = getRecord().getData(); + + format = fr.getNumberFormat(getXFIndex()); + + if (format == null) + { + format = defaultFormat; + } + + value = DoubleHelper.getIEEEDouble(data, 6); + } + + /** + * Interface method which returns the value + * + * @return the last calculated value of the formula + */ + public double getValue() + { + return value; + } + + /** + * Returns the numerical value as a string + * + * @return The numerical value of the formula as a string + */ + public String getContents() + { + return !Double.isNaN(value) ? format.format(value) : ""; + } + + /** + * Returns the cell type + * + * @return The cell type + */ + public CellType getType() + { + return CellType.NUMBER_FORMULA; + } + + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array. Used when copying spreadsheets + * + * @return the raw record data + */ + public byte[] getFormulaData() throws FormulaException + { + if (!getSheet().getWorkbookBof().isBiff8()) + { + throw new FormulaException(FormulaException.BIFF8_SUPPORTED); + } + + // Lop off the standard information + byte[] d = new byte[data.length - 6]; + System.arraycopy(data, 6, d, 0, data.length - 6); + + return d; + } + + /** + * Gets the formula as an excel string + * + * @return the formula as an excel string + * @exception FormulaException + */ + public String getFormula() throws FormulaException + { + if (formulaString == null) + { + byte[] tokens = new byte[data.length - 22]; + System.arraycopy(data, 22, tokens, 0, tokens.length); + FormulaParser fp = new FormulaParser + (tokens, this, externalSheet, nameTable, + getSheet().getWorkbook().getSettings()); + fp.parse(); + formulaString = fp.getFormula(); + } + + return formulaString; + } + + /** + * Gets the NumberFormat used to format this cell. This is the java + * equivalent of the Excel format + * + * @return the NumberFormat used to format the cell + */ + public NumberFormat getNumberFormat() + { + return format; + } +} Index: 3rdParty_sources/jexcelapi/jxl/read/biff/NumberRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/NumberRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/NumberRecord.java 17 Aug 2012 14:50:58 -0000 1.1 @@ -0,0 +1,123 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import java.text.DecimalFormat; +import java.text.NumberFormat; + +import common.Logger; + +import jxl.CellType; +import jxl.NumberCell; +import jxl.biff.DoubleHelper; +import jxl.biff.FormattingRecords; + +/** + * A number record. This is stored as 8 bytes, as opposed to the + * 4 byte RK record + */ +class NumberRecord extends CellValue implements NumberCell +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(NumberRecord.class); + + /** + * The value + */ + private double value; + + /** + * The java equivalent of the excel format + */ + private NumberFormat format; + + /** + * The formatter to convert the value into a string + */ + private static DecimalFormat defaultFormat = new DecimalFormat("#.###"); + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param fr the available formats + * @param si the sheet + */ + public NumberRecord(Record t, FormattingRecords fr, SheetImpl si) + { + super(t, fr, si); + byte[] data = getRecord().getData(); + + value = DoubleHelper.getIEEEDouble(data, 6); + + // Now get the number format + format = fr.getNumberFormat(getXFIndex()); + if (format == null) + { + format = defaultFormat; + } + } + + /** + * Accessor for the value + * + * @return the value + */ + public double getValue() + { + return value; + } + + /** + * Returns the contents of this cell as a string + * + * @return the value formatted into a string + */ + public String getContents() + { + return format.format(value); + } + + /** + * Accessor for the cell type + * + * @return the cell type + */ + public CellType getType() + { + return CellType.NUMBER; + } + + /** + * Gets the NumberFormat used to format this cell. This is the java + * equivalent of the Excel format + * + * @return the NumberFormat used to format the cell + */ + public NumberFormat getNumberFormat() + { + return format; + } +} + + + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/NumberValue.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/NumberValue.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/NumberValue.java 17 Aug 2012 14:50:57 -0000 1.1 @@ -0,0 +1,255 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import java.text.DecimalFormat; +import java.text.NumberFormat; + +import jxl.CellFeatures; +import jxl.CellType; +import jxl.NumberCell; +import jxl.biff.FormattingRecords; +import jxl.format.CellFormat; + +/** + * A numerical cell value, initialized indirectly from a multiple biff record + * rather than directly from the binary data + */ +class NumberValue implements NumberCell, CellFeaturesAccessor +{ + /** + * The row containing this number + */ + private int row; + /** + * The column containing this number + */ + private int column; + /** + * The value of this number + */ + private double value; + + /** + * The cell format + */ + private NumberFormat format; + + /** + * The raw cell format + */ + private CellFormat cellFormat; + + /** + * The cell features + */ + private CellFeatures features; + + /** + * The index to the XF Record + */ + private int xfIndex; + + /** + * A handle to the formatting records + */ + private FormattingRecords formattingRecords; + + /** + * A flag to indicate whether this object's formatting things have + * been initialized + */ + private boolean initialized; + + /** + * A handle to the sheet + */ + private SheetImpl sheet; + + /** + * The format in which to return this number as a string + */ + private static DecimalFormat defaultFormat = new DecimalFormat("#.###"); + + /** + * Constructs this number + * + * @param r the zero based row + * @param c the zero base column + * @param val the value + * @param xfi the xf index + * @param fr the formatting records + * @param si the sheet + */ + public NumberValue(int r, int c, double val, + int xfi, + FormattingRecords fr, + SheetImpl si) + { + row = r; + column = c; + value = val; + format = defaultFormat; + xfIndex = xfi; + formattingRecords = fr; + sheet = si; + initialized = false; + } + + /** + * Sets the format for the number based on the Excel spreadsheets' format. + * This is called from SheetImpl when it has been definitely established + * that this cell is a number and not a date + * + * @param f the format + */ + final void setNumberFormat(NumberFormat f) + { + if (f != null) + { + format = f; + } + } + + /** + * Accessor for the row + * + * @return the zero based row + */ + public final int getRow() + { + return row; + } + + /** + * Accessor for the column + * + * @return the zero based column + */ + public final int getColumn() + { + return column; + } + + /** + * Accessor for the value + * + * @return the value + */ + public double getValue() + { + return value; + } + + /** + * Accessor for the contents as a string + * + * @return the value as a string + */ + public String getContents() + { + return format.format(value); + } + + /** + * Accessor for the cell type + * + * @return the cell type + */ + public CellType getType() + { + return CellType.NUMBER; + } + + /** + * Gets the cell format + * + * @return the cell format + */ + public CellFormat getCellFormat() + { + if (!initialized) + { + cellFormat = formattingRecords.getXFRecord(xfIndex); + initialized = true; + } + + return cellFormat; + } + + /** + * Determines whether or not this cell has been hidden + * + * @return TRUE if this cell has been hidden, FALSE otherwise + */ + public boolean isHidden() + { + ColumnInfoRecord cir = sheet.getColumnInfo(column); + + if (cir != null && cir.getWidth() == 0) + { + return true; + } + + RowRecord rr = sheet.getRowInfo(row); + + if (rr != null && (rr.getRowHeight() == 0 || rr.isCollapsed())) + { + return true; + } + + return false; + } + + /** + * Gets the NumberFormat used to format this cell. This is the java + * equivalent of the Excel format + * + * @return the NumberFormat used to format the cell + */ + public NumberFormat getNumberFormat() + { + return format; + } + + /** + * Accessor for the cell features + * + * @return the cell features or NULL if this cell doesn't have any + */ + public CellFeatures getCellFeatures() + { + return features; + } + + /** + * Sets the cell features + * + * @param cf the cell features + */ + public void setCellFeatures(CellFeatures cf) + { + features = cf; + } + +} + + + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/PLSRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/PLSRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/PLSRecord.java 17 Aug 2012 14:50:56 -0000 1.1 @@ -0,0 +1,48 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.RecordData; + +/** + * The environment specific print record + */ +public class PLSRecord extends RecordData +{ + /** + * Constructs this object from the raw data + * + * @param r the raw data + */ + public PLSRecord(Record r) + { + super(r); + } + + /** + * Gets the data + * + * @return the binary data + */ + public byte[] getData() + { + return getRecord().getData(); + } +} Index: 3rdParty_sources/jexcelapi/jxl/read/biff/PaletteRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/PaletteRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/PaletteRecord.java 17 Aug 2012 14:50:57 -0000 1.1 @@ -0,0 +1,48 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.RecordData; + +/** + * A password record + */ +public class PaletteRecord extends RecordData +{ + /** + * Constructor + * + * @param t the raw bytes + */ + PaletteRecord(Record t) + { + super(t); + } + + /** + * Accessor for the binary data - used when copying + * + * @return the binary data + */ + public byte[] getData() + { + return getRecord().getData(); + } +} Index: 3rdParty_sources/jexcelapi/jxl/read/biff/PaneRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/PaneRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/PaneRecord.java 17 Aug 2012 14:50:57 -0000 1.1 @@ -0,0 +1,86 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import common.Logger; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; + +/** + * Contains the cell dimensions of this worksheet + */ +class PaneRecord extends RecordData +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(PaneRecord.class); + + /** + * The number of rows visible in the top left pane + */ + private int rowsVisible; + /** + * The number of columns visible in the top left pane + */ + private int columnsVisible; + + /** + * Constructs the dimensions from the raw data + * + * @param t the raw data + */ + public PaneRecord(Record t) + { + super(t); + byte[] data = t.getData(); + + columnsVisible = IntegerHelper.getInt(data[0], data[1]); + rowsVisible = IntegerHelper.getInt(data[2], data[3]); + } + + /** + * Accessor for the number of rows in the top left pane + * + * @return the number of rows visible in the top left pane + */ + public final int getRowsVisible() + { + return rowsVisible; + } + + /** + * Accessor for the numbe rof columns visible in the top left pane + * + * @return the number of columns visible in the top left pane + */ + public final int getColumnsVisible() + { + return columnsVisible; + } +} + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/PasswordException.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/PasswordException.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/PasswordException.java 17 Aug 2012 14:50:56 -0000 1.1 @@ -0,0 +1,35 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +/** + * A properly typed exception in case consumers of the API specifically + * wish to handle the case when the workbook is password protected + */ +public class PasswordException extends BiffException +{ + /** + * Constructor + */ + public PasswordException() + { + super(passwordProtected); + } +} Index: 3rdParty_sources/jexcelapi/jxl/read/biff/PasswordRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/PasswordRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/PasswordRecord.java 17 Aug 2012 14:50:57 -0000 1.1 @@ -0,0 +1,62 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.Type; + +/** + * A password record + */ +class PasswordRecord extends RecordData +{ + /** + * The password + */ + private String password; + /** + * The binary data + */ + private int passwordHash; + + /** + * Constructor + * + * @param t the raw bytes + */ + public PasswordRecord(Record t) + { + super(Type.PASSWORD); + + byte[] data = t.getData(); + passwordHash = IntegerHelper.getInt(data[0], data[1]); + } + + /** + * Gets the binary data for output to file + * + * @return the password hash + */ + public int getPasswordHash() + { + return passwordHash; + } +} Index: 3rdParty_sources/jexcelapi/jxl/read/biff/PrintGridLinesRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/PrintGridLinesRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/PrintGridLinesRecord.java 17 Aug 2012 14:50:56 -0000 1.1 @@ -0,0 +1,57 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.RecordData; + +/** + * Contains the print grid lines option of this worksheet + */ +class PrintGridLinesRecord extends RecordData +{ + /** + * print grid lines flag + */ + private boolean printGridLines; + + /** + * Constructs the value from the raw data + * + * @param pgl the raw data + */ + public PrintGridLinesRecord(Record pgl) + { + super(pgl); + byte[] data = pgl.getData(); + + printGridLines = (data[0] == 1 ? true : false); + } + + /** + * Accessor for the print grid lines flag + * + * @return TRUE to print grid lines, FALSE otherwise + */ + public boolean getPrintGridLines() + { + return printGridLines; + } + +} Index: 3rdParty_sources/jexcelapi/jxl/read/biff/PrintHeadersRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/PrintHeadersRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/PrintHeadersRecord.java 17 Aug 2012 14:50:56 -0000 1.1 @@ -0,0 +1,57 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.RecordData; + +/** + * Contains the print grid lines option of this worksheet + */ +class PrintHeadersRecord extends RecordData +{ + /** + * print grid lines flag + */ + private boolean printHeaders; + + /** + * Constructs the value from the raw data + * + * @param ph the raw data + */ + public PrintHeadersRecord(Record ph) + { + super(ph); + byte[] data = ph.getData(); + + printHeaders = (data[0] == 1 ? true : false); + } + + /** + * Accessor for the print headers flag + * + * @return TRUE to print headers, FALSE otherwise + */ + public boolean getPrintHeaders() + { + return printHeaders; + } + +} Index: 3rdParty_sources/jexcelapi/jxl/read/biff/ProtectRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/ProtectRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/ProtectRecord.java 17 Aug 2012 14:50:57 -0000 1.1 @@ -0,0 +1,70 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; + +/** + * A record detailing whether the sheet is protected + */ +class ProtectRecord extends RecordData +{ + /** + * Protected flag + */ + private boolean prot; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + */ + ProtectRecord(Record t) + { + super(t); + byte[] data = getRecord().getData(); + + int protflag = IntegerHelper.getInt(data[0], data[1]); + + prot = (protflag == 1); + } + + /** + * Returns the protected flag + * + * @return TRUE if this is protected, FALSE otherwise + */ + boolean isProtected() + { + return prot; + } + + +} + + + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/RKHelper.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/RKHelper.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/RKHelper.java 17 Aug 2012 14:50:58 -0000 1.1 @@ -0,0 +1,70 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +/** + * Helper to convert an RK number into a double or an integer + */ +final class RKHelper +{ + /** + * Private constructor to prevent instantiation + */ + private RKHelper() + { + } + + /** + * Converts excel's internal RK format into a double value + * + * @param rk the rk number in bits + * @return the double representation + */ + public static double getDouble(int rk) + { + if ((rk & 0x02) != 0) + { + int intval = rk >> 2; + + double value = intval; + if ((rk & 0x01) != 0) + { + value /= 100; + } + + return value; + } + else + { + long valbits = (rk & 0xfffffffc); + valbits <<= 32; + double value = Double.longBitsToDouble(valbits); + + if ((rk & 0x01) != 0) + { + value /= 100; + } + + return value; + } + } +} + + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/RKRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/RKRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/RKRecord.java 17 Aug 2012 14:50:57 -0000 1.1 @@ -0,0 +1,122 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import java.text.DecimalFormat; +import java.text.NumberFormat; + +import common.Logger; + +import jxl.CellType; +import jxl.NumberCell; +import jxl.biff.FormattingRecords; +import jxl.biff.IntegerHelper; + +/** + * An individual RK record + */ +class RKRecord extends CellValue implements NumberCell +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(RKRecord.class); + + /** + * The value + */ + private double value; + + /** + * The java equivalent of the excel format + */ + private NumberFormat format; + + /** + * The formatter to convert the value into a string + */ + private static DecimalFormat defaultFormat = new DecimalFormat("#.###"); + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param fr the available cell formats + * @param si the sheet + */ + public RKRecord(Record t, FormattingRecords fr, SheetImpl si) + { + super(t, fr, si); + byte[] data = getRecord().getData(); + int rknum = IntegerHelper.getInt(data[6], data[7], data[8], data[9]); + value = RKHelper.getDouble(rknum); + + // Now get the number format + format = fr.getNumberFormat(getXFIndex()); + if (format == null) + { + format = defaultFormat; + } + } + + /** + * Accessor for the value + * + * @return the value + */ + public double getValue() + { + return value; + } + + /** + * Returns the contents of this cell as a string + * + * @return the value formatted into a string + */ + public String getContents() + { + return format.format(value); + } + + /** + * Accessor for the cell type + * + * @return the cell type + */ + public CellType getType() + { + return CellType.NUMBER; + } + + /** + * Gets the NumberFormat used to format this cell. This is the java + * equivalent of the Excel format + * + * @return the NumberFormat used to format the cell + */ + public NumberFormat getNumberFormat() + { + return format; + } +} + + + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/RStringRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/RStringRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/RStringRecord.java 17 Aug 2012 14:50:55 -0000 1.1 @@ -0,0 +1,97 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import jxl.CellType; +import jxl.LabelCell; +import jxl.WorkbookSettings; +import jxl.biff.FormattingRecords; +import jxl.biff.IntegerHelper; +import jxl.biff.StringHelper; + +/** + * A label which is stored in the cell + */ +class RStringRecord extends CellValue implements LabelCell +{ + /** + * The length of the label in characters + */ + private int length; + /** + * The label + */ + private String string; + + /** + * Dummy indicators for overloading the constructor + */ + private static class Biff7 {}; + public static Biff7 biff7 = new Biff7(); + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param fr the formatting records + * @param si the sheet + * @param ws the workbook settings + * @param dummy dummy overload to indicate a biff 7 workbook + */ + public RStringRecord(Record t, FormattingRecords fr, + SheetImpl si, WorkbookSettings ws, Biff7 dummy) + { + super(t, fr, si); + byte[] data = getRecord().getData(); + length = IntegerHelper.getInt(data[6], data[7]); + + string = StringHelper.getString(data, length, 8, ws); + } + + /** + * Gets the label + * + * @return the label + */ + public String getString() + { + return string; + } + + /** + * Gets the cell contents as a string + * + * @return the label + */ + public String getContents() + { + return string; + } + + /** + * Accessor for the cell type + * + * @return the cell type + */ + public CellType getType() + { + return CellType.LABEL; + } +} Index: 3rdParty_sources/jexcelapi/jxl/read/biff/Record.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/Record.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/Record.java 17 Aug 2012 14:50:55 -0000 1.1 @@ -0,0 +1,184 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import java.util.ArrayList; + +import common.Logger; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; + + +/** + * A container for the raw record data within a biff file + */ +public final class Record +{ + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(Record.class); + + /** + * The excel biff code + */ + private int code; + /** + * The data type + */ + private Type type; + /** + * The length of this record + */ + private int length; + /** + * A pointer to the beginning of the actual data + */ + private int dataPos; + /** + * A handle to the excel 97 file + */ + private File file; + /** + * The raw data within this record + */ + private byte[] data; + + /** + * Any continue records + */ + private ArrayList continueRecords; + + /** + * Constructor + * + * @param offset the offset in the raw file + * @param f the excel 97 biff file + * @param d the data record + */ + Record(byte[] d, int offset, File f) + { + code = IntegerHelper.getInt(d[offset], d[offset + 1]); + length = IntegerHelper.getInt(d[offset + 2], d[offset + 3]); + file = f; + file.skip(4); + dataPos = f.getPos(); + file.skip(length); + type = Type.getType(code); + } + + /** + * Gets the biff type + * + * @return the biff type + */ + public Type getType() + { + return type; + } + + /** + * Gets the length of the record + * + * @return the length of the record + */ + public int getLength() + { + return length; + } + + /** + * Gets the data portion of the record + * + * @return the data portion of the record + */ + public byte[] getData() + { + if (data == null) + { + data = file.read(dataPos, length); + } + + // copy in any data from the continue records + if (continueRecords != null) + { + int size = 0; + byte[][] contData = new byte[continueRecords.size()][]; + for (int i = 0; i < continueRecords.size(); i++) + { + Record r = (Record) continueRecords.get(i); + contData[i] = r.getData(); + byte[] d2 = contData[i]; + size += d2.length; + } + + byte[] d3 = new byte[data.length + size]; + System.arraycopy(data, 0, d3, 0, data.length); + int pos = data.length; + for (int i = 0; i < contData.length; i++) + { + byte[] d2 = contData[i]; + System.arraycopy(d2, 0, d3, pos, d2.length); + pos += d2.length; + } + + data = d3; + } + + return data; + } + + /** + * The excel 97 code + * + * @return the excel code + */ + public int getCode() + { + return code; + } + + /** + * In the case of dodgy records, this method may be called to forcibly + * set the type in order to continue processing + * + * @param t the forcibly overridden type + */ + void setType(Type t) + { + type = t; + } + + /** + * Adds a continue record to this data + * + * @param d the continue record + */ + public void addContinueRecord(Record d) + { + if (continueRecords == null) + { + continueRecords = new ArrayList(); + } + + continueRecords.add(d); + } +} Index: 3rdParty_sources/jexcelapi/jxl/read/biff/RightMarginRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/RightMarginRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/RightMarginRecord.java 17 Aug 2012 14:50:55 -0000 1.1 @@ -0,0 +1,37 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.Type; + +/** + * Record for the left margin settings + */ +class RightMarginRecord extends MarginRecord +{ + /** + * Constructor + * @param r the record + */ + RightMarginRecord(Record r) + { + super(Type.RIGHTMARGIN, r); + } +} Index: 3rdParty_sources/jexcelapi/jxl/read/biff/RowRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/RowRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/RowRecord.java 17 Aug 2012 14:50:56 -0000 1.1 @@ -0,0 +1,191 @@ +/********************************************************************* +* +* Copyright (C) 2001 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import common.Logger; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; + +/** + * A row record + */ +public class RowRecord extends RecordData +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(RowRecord.class); + + /** + * The number of this row + */ + private int rowNumber; + /** + * The height of this row + */ + private int rowHeight; + /** + * Flag to indicate whether this row is collapsed or not + */ + private boolean collapsed; + /** + * Indicates whether this row has an explicit default format + */ + private boolean defaultFormat; + /** + * Indicates whether the row record height matches the default font height + */ + private boolean matchesDefFontHeight; + /** + * The (default) xf index for cells on this row + */ + private int xfIndex; + + /** + * The outline level of the row + */ + private int outlineLevel; + + /** + * Is this the icon indicator row of a group? + */ + private boolean groupStart; + + /** + * Indicates that the row is default height + */ + private static final int defaultHeightIndicator = 0xff; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + */ + RowRecord(Record t) + { + super(t); + + byte[] data = getRecord().getData(); + rowNumber = IntegerHelper.getInt(data[0], data[1]); + rowHeight = IntegerHelper.getInt(data[6], data[7]); + + int options = IntegerHelper.getInt(data[12], data[13], + data[14], data[15]); + outlineLevel = (options & 0x7); + groupStart = (options & 0x10) != 0; + collapsed = (options & 0x20) != 0; + matchesDefFontHeight = (options & 0x40) == 0; + defaultFormat = (options & 0x80) != 0; + xfIndex = (options & 0x0fff0000) >> 16; + } + + /** + * Interrogates whether this row is of default height + * + * @return TRUE if this is set to the default height, FALSE otherwise + */ + boolean isDefaultHeight() + { + return rowHeight == defaultHeightIndicator; + } + + /** + * Interrogates this row to see whether it matches the default font height + * + * @return TRUE if this matches the default font height, FALSE otherwise + */ + public boolean matchesDefaultFontHeight() + { + return matchesDefFontHeight; + } + + /** + * Gets the row number + * + * @return the number of this row + */ + public int getRowNumber() + { + return rowNumber; + } + + /** + * Accessor for the row's outline level + * + * @return the row's outline level + */ + public int getOutlineLevel() + { + return outlineLevel; + } + + /** + * Accessor for row's groupStart value + * + * @return the row's groupStart value + */ + public boolean getGroupStart() + { + return groupStart; + } + + /** + * Gets the height of the row + * + * @return the row height + */ + public int getRowHeight() + { + return rowHeight; + } + + /** + * Queries whether the row is collapsed + * + * @return the collapsed indicator + */ + public boolean isCollapsed() + { + return collapsed; + } + + /** + * Gets the default xf index for this row + * + * @return the xf index + */ + public int getXFIndex() + { + return xfIndex; + } + + /** + * Queries whether the row has a specific default cell format applied + * + * @return the default cell format + */ + public boolean hasDefaultFormat() + { + return defaultFormat; + } +} + + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/SCLRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/SCLRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/SCLRecord.java 17 Aug 2012 14:50:57 -0000 1.1 @@ -0,0 +1,64 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan, Adam Caldwell +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.Type; + +/** + * Class containing the zoom factor for display + */ +class SCLRecord extends RecordData +{ + /** + * The numerator of the zoom + */ + private int numerator; + + /** + * The denominator of the zoom + */ + private int denominator; + + /** + * Constructs this record from the raw data + * @param r the record + */ + protected SCLRecord(Record r) + { + super(Type.SCL); + + byte[] data = r.getData(); + + numerator = IntegerHelper.getInt(data[0], data[1]); + denominator = IntegerHelper.getInt(data[2], data[3]); + } + + /** + * Accessor for the zoom factor + * + * @return the zoom factor as the nearest integer percentage + */ + public int getZoomFactor() + { + return numerator * 100 / denominator; + } +} Index: 3rdParty_sources/jexcelapi/jxl/read/biff/SSTRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/SSTRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/SSTRecord.java 17 Aug 2012 14:50:56 -0000 1.1 @@ -0,0 +1,431 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import common.Assert; + +import jxl.WorkbookSettings; +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.StringHelper; + +/** + * Holds all the strings in the shared string table + */ +class SSTRecord extends RecordData +{ + /** + * The total number of strings in this table + */ + private int totalStrings; + /** + * The number of unique strings + */ + private int uniqueStrings; + /** + * The shared strings + */ + private String[] strings; + /** + * The array of continuation breaks + */ + private int[] continuationBreaks; + + /** + * A holder for a byte array + */ + private static class ByteArrayHolder + { + /** + * the byte holder + */ + public byte[] bytes; + } + + /** + * A holder for a boolean + */ + private static class BooleanHolder + { + /** + * the holder holder + */ + public boolean value; + } + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param continuations the continuations + * @param ws the workbook settings + */ + public SSTRecord(Record t, Record[] continuations, WorkbookSettings ws) + { + super(t); + + // If a continue record appears in the middle of + // a string, then the encoding character is repeated + + // Concatenate everything into one big bugger of a byte array + int totalRecordLength = 0; + + for (int i = 0; i < continuations.length; i++) + { + totalRecordLength += continuations[i].getLength(); + } + totalRecordLength += getRecord().getLength(); + + byte[] data = new byte[totalRecordLength]; + + // First the original data gets put in + int pos = 0; + System.arraycopy(getRecord().getData(), 0, + data, 0, getRecord().getLength()); + pos += getRecord().getLength(); + + // Now copy in everything else. + continuationBreaks = new int[continuations.length]; + Record r = null; + for (int i = 0; i < continuations.length; i++) + { + r = continuations[i]; + System.arraycopy(r.getData(), 0, + data, pos, + r.getLength()); + continuationBreaks[i] = pos; + pos += r.getLength(); + } + + totalStrings = IntegerHelper.getInt(data[0], data[1], + data[2], data[3]); + uniqueStrings = IntegerHelper.getInt(data[4], data[5], + data[6], data[7]); + + strings = new String[uniqueStrings]; + readStrings(data, 8, ws); + } + + /** + * Reads in all the strings from the raw data + * + * @param data the raw data + * @param offset the offset + * @param ws the workbook settings + */ + private void readStrings(byte[] data, int offset, WorkbookSettings ws) + { + int pos = offset; + int numChars; + byte optionFlags; + String s = null; + boolean asciiEncoding = false; + boolean richString = false; + boolean extendedString = false; + int formattingRuns = 0; + int extendedRunLength = 0; + + for (int i = 0; i < uniqueStrings; i++) + { + // Read in the number of characters + numChars = IntegerHelper.getInt(data[pos], data[pos + 1]); + pos += 2; + optionFlags = data[pos]; + pos++; + + // See if it is an extended string + extendedString = ((optionFlags & 0x04) != 0); + + // See if string contains formatting information + richString = ((optionFlags & 0x08) != 0); + + if (richString) + { + // Read in the crun + formattingRuns = IntegerHelper.getInt(data[pos], data[pos + 1]); + pos += 2; + } + + if (extendedString) + { + // Read in cchExtRst + extendedRunLength = IntegerHelper.getInt + (data[pos], data[pos + 1], data[pos + 2], data[pos + 3]); + pos += 4; + } + + // See if string is ASCII (compressed) or unicode + asciiEncoding = ((optionFlags & 0x01) == 0); + + ByteArrayHolder bah = new ByteArrayHolder(); + BooleanHolder bh = new BooleanHolder(); + bh.value = asciiEncoding; + pos += getChars(data, bah, pos, bh, numChars); + asciiEncoding = bh.value; + + if (asciiEncoding) + { + s = StringHelper.getString(bah.bytes, numChars, 0, ws); + } + else + { + s = StringHelper.getUnicodeString(bah.bytes, numChars, 0); + } + + strings[i] = s; + + // For rich strings, skip over the formatting runs + if (richString) + { + pos += 4 * formattingRuns; + } + + // For extended strings, skip over the extended string data + if (extendedString) + { + pos += extendedRunLength; + } + + if (pos > data.length) + { + Assert.verify(false, "pos exceeds record length"); + } + } + } + + /** + * Gets the chars in the ascii array, taking into account continuation + * breaks + * + * @param source the original source + * @param bah holder for the new byte array + * @param pos the current position in the source + * @param ascii holder for a return ascii flag + * @param numChars the number of chars in the string + * @return the number of bytes read from the source + */ + private int getChars(byte[] source, + ByteArrayHolder bah, + int pos, + BooleanHolder ascii, + int numChars) + { + int i = 0; + boolean spansBreak = false; + + if (ascii.value) + { + bah.bytes = new byte[numChars]; + } + else + { + bah.bytes = new byte[numChars * 2]; + } + + while (i < continuationBreaks.length && !spansBreak) + { + spansBreak = pos <= continuationBreaks[i] && + (pos + bah.bytes.length > continuationBreaks[i]); + + if (!spansBreak) + { + i++; + } + } + + // If it doesn't span a break simply do an array copy into the + // destination array and finish + if (!spansBreak) + { + System.arraycopy(source, pos, bah.bytes, 0, bah.bytes.length); + return bah.bytes.length; + } + + // Copy the portion before the break pos into the array + int breakpos = continuationBreaks[i]; + System.arraycopy(source, pos, bah.bytes, 0, breakpos - pos); + + int bytesRead = breakpos - pos; + int charsRead; + if (ascii.value) + { + charsRead = bytesRead; + } + else + { + charsRead = bytesRead / 2; + } + + bytesRead += getContinuedString(source, + bah, + bytesRead, + i, + ascii, + numChars - charsRead); + return bytesRead; + } + + /** + * Gets the rest of the string after a continuation break + * + * @param source the original bytes + * @param bah the holder for the new bytes + * @param destPos the des pos + * @param contBreakIndex the index of the continuation break + * @param ascii the ascii flag holder + * @param charsLeft the number of chars left in the array + * @return the number of bytes read in the continued string + */ + private int getContinuedString(byte[] source, + ByteArrayHolder bah, + int destPos, + int contBreakIndex, + BooleanHolder ascii, + int charsLeft) + { + int breakpos = continuationBreaks[contBreakIndex]; + int bytesRead = 0; + + while (charsLeft > 0) + { + Assert.verify(contBreakIndex < continuationBreaks.length, + "continuation break index"); + + if (ascii.value && source[breakpos] == 0) + { + // The string is consistently ascii throughout + + int length = contBreakIndex == continuationBreaks.length - 1 ? + charsLeft : + Math.min + (charsLeft, + continuationBreaks[contBreakIndex + 1] - breakpos - 1); + + System.arraycopy(source, + breakpos + 1, + bah.bytes, + destPos, + length); + destPos += length; + bytesRead += length + 1; + charsLeft -= length; + ascii.value = true; + } + else if (!ascii.value && source[breakpos] != 0) + { + // The string is Unicode throughout + + int length = contBreakIndex == continuationBreaks.length - 1 ? + charsLeft * 2 : + Math.min + (charsLeft * 2, + continuationBreaks[contBreakIndex + 1] - breakpos - 1); + + // It looks like the string continues as Unicode too. That's handy + System.arraycopy(source, + breakpos + 1, + bah.bytes, + destPos, + length); + + destPos += length; + bytesRead += length + 1; + charsLeft -= length / 2; + ascii.value = false; + } + else if (!ascii.value && source[breakpos] == 0) + { + // Bummer - the string starts off as Unicode, but after the + // continuation it is in straightforward ASCII encoding + int chars = contBreakIndex == continuationBreaks.length - 1 ? + charsLeft: + Math.min + (charsLeft, + continuationBreaks[contBreakIndex + 1] - breakpos - 1); + + for (int j = 0; j < chars; j++) + { + bah.bytes[destPos] = source[breakpos + j + 1]; + destPos += 2; + } + + bytesRead += chars + 1; + charsLeft -= chars; + ascii.value = false; + } + else + { + // Double Bummer - the string starts off as ASCII, but after the + // continuation it is in Unicode. This impacts the allocated array + + // Reallocate what we have of the byte array so that it is all + // Unicode + byte[] oldBytes = bah.bytes; + bah.bytes = new byte[destPos * 2 + charsLeft * 2]; + for (int j = 0; j < destPos; j++) + { + bah.bytes[j * 2] = oldBytes[j]; + } + + destPos = destPos * 2; + + int length = contBreakIndex == continuationBreaks.length - 1 ? + charsLeft * 2 : + Math.min + (charsLeft * 2, + continuationBreaks[contBreakIndex + 1] - breakpos - 1); + + System.arraycopy(source, + breakpos + 1, + bah.bytes, + destPos, + length); + + destPos += length; + bytesRead += length + 1; + charsLeft -= length / 2; + ascii.value = false; + } + + contBreakIndex++; + + if (contBreakIndex < continuationBreaks.length) + { + breakpos = continuationBreaks[contBreakIndex]; + } + } + + return bytesRead; + } + + /** + * Gets the string at the specified position + * + * @param index the index of the string to return + * @return the strings + */ + public String getString(int index) + { + Assert.verify(index < uniqueStrings); + return strings[index]; + } +} + + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/SaveRecalcRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/SaveRecalcRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/SaveRecalcRecord.java 17 Aug 2012 14:50:57 -0000 1.1 @@ -0,0 +1,64 @@ +/********************************************************************* +* +* Copyright (C) 2005 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import common.Logger; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; + +/** + * A calculation mode record + */ +class SaveRecalcRecord extends RecordData +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(SaveRecalcRecord.class); + + /** + * The calculation mode + */ + private boolean recalculateOnSave; + + /** + * Constructor + * + * @param t the record + */ + public SaveRecalcRecord(Record t) + { + super(t); + byte[] data = t.getData(); + int mode = IntegerHelper.getInt(data[0], data[1]); + recalculateOnSave = (mode == 1); + } + + /** + * Accessor for the recalculate on save mode + * + * @return the recalculate on save mode + */ + public boolean getRecalculateOnSave() + { + return recalculateOnSave; + } +} Index: 3rdParty_sources/jexcelapi/jxl/read/biff/SetupRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/SetupRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/SetupRecord.java 17 Aug 2012 14:50:56 -0000 1.1 @@ -0,0 +1,272 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import common.Logger; + +import jxl.biff.DoubleHelper; +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.Type; + +/** + * Contains the page set up for a sheet + */ +public class SetupRecord extends RecordData +{ + // The logger + private static Logger logger = Logger.getLogger(SetupRecord.class); + + /** + * The raw data + */ + private byte[] data; + + /** + * The orientation flag + */ + private boolean portraitOrientation; + + /** + * The Page Order flag + */ + private boolean pageOrder; + + /** + * The header margin + */ + private double headerMargin; + + /** + * The footer margin + */ + private double footerMargin; + + /** + * The paper size + */ + private int paperSize; + + /** + * The scale factor + */ + private int scaleFactor; + + /** + * The page start + */ + private int pageStart; + + /** + * The fit width + */ + private int fitWidth; + + /** + * The fit height + */ + private int fitHeight; + + /** + * The horizontal print resolution + */ + private int horizontalPrintResolution; + + /** + * The vertical print resolution + */ + private int verticalPrintResolution; + + /** + * The number of copies + */ + private int copies; + + /** + * Indicates whether the setup data should be initiliazed in the setup + * box + */ + private boolean initialized; + + /** + * Constructor which creates this object from the binary data + * + * @param t the record + */ + SetupRecord(Record t) + { + super(Type.SETUP); + + data = t.getData(); + + paperSize = IntegerHelper.getInt(data[0], data[1]); + scaleFactor = IntegerHelper.getInt(data[2], data[3]); + pageStart = IntegerHelper.getInt(data[4], data[5]); + fitWidth = IntegerHelper.getInt(data[6], data[7]); + fitHeight = IntegerHelper.getInt(data[8], data[9]); + horizontalPrintResolution = IntegerHelper.getInt(data[12], data[13]); + verticalPrintResolution = IntegerHelper.getInt(data[14], data[15]); + copies = IntegerHelper.getInt(data[32], data[33]); + + headerMargin = DoubleHelper.getIEEEDouble(data, 16); + footerMargin = DoubleHelper.getIEEEDouble(data, 24); + + + + int grbit = IntegerHelper.getInt(data[10], data[11]); + pageOrder = ((grbit & 0x01) != 0); + portraitOrientation = ((grbit & 0x02) != 0); + initialized = ( (grbit & 0x04) == 0); + } + + /** + * Accessor for the orientation. Called when copying sheets + * + * @return TRUE if the orientation is portrait, FALSE if it is landscape + */ + public boolean isPortrait() + { + return portraitOrientation; + } + + + /** + * Accessor for the page order. Called when copying sheets + * + * @return TRUE if the page order is Left to Right, then Down, otherwise + * FALSE + */ + public boolean isRightDown() + { + return pageOrder; + } + + /** + * Accessor for the header. Called when copying sheets + * + * @return the header margin + */ + public double getHeaderMargin() + { + return headerMargin; + } + + /** + * Accessor for the footer. Called when copying sheets + * + * @return the footer margin + */ + public double getFooterMargin() + { + return footerMargin; + } + + /** + * Accessor for the paper size. Called when copying sheets + * + * @return the footer margin + */ + public int getPaperSize() + { + return paperSize; + } + + /** + * Accessor for the scale factor. Called when copying sheets + * + * @return the scale factor + */ + public int getScaleFactor() + { + return scaleFactor; + } + + /** + * Accessor for the page height. called when copying sheets + * + * @return the page to start printing at + */ + public int getPageStart() + { + return pageStart; + } + + /** + * Accessor for the fit width. Called when copying sheets + * + * @return the fit width + */ + public int getFitWidth() + { + return fitWidth; + } + + /** + * Accessor for the fit height. Called when copying sheets + * + * @return the fit height + */ + public int getFitHeight() + { + return fitHeight; + } + + /** + * The horizontal print resolution. Called when copying sheets + * + * @return the horizontal print resolution + */ + public int getHorizontalPrintResolution() + { + return horizontalPrintResolution; + } + + /** + * Accessor for the vertical print resolution. Called when copying sheets + * + * @return an vertical print resolution + */ + public int getVerticalPrintResolution() + { + return verticalPrintResolution; + } + + /** + * Accessor for the number of copies + * + * @return the number of copies + */ + public int getCopies() + { + return copies; + } + + /** + * Accessor for the initialized flag + * + * @return whether the print page setup should be initialized in the dialog + * box + */ + public boolean getInitialized() + { + return initialized; + } + +} Index: 3rdParty_sources/jexcelapi/jxl/read/biff/SharedBooleanFormulaRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/SharedBooleanFormulaRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/SharedBooleanFormulaRecord.java 17 Aug 2012 14:50:56 -0000 1.1 @@ -0,0 +1,156 @@ +/********************************************************************* +* +* Copyright (C) 2005 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import common.Assert; +import common.Logger; + +import jxl.BooleanCell; +import jxl.BooleanFormulaCell; +import jxl.CellType; +import jxl.biff.DoubleHelper; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.IntegerHelper; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; + +/** + * A shared boolean formula record + */ +public class SharedBooleanFormulaRecord extends BaseSharedFormulaRecord + implements BooleanCell, FormulaData, BooleanFormulaCell +{ + /** + * The logger + */ + private static Logger logger = + Logger.getLogger(SharedBooleanFormulaRecord.class); + + /** + * The boolean value of this cell. If this cell represents an error, + * this will be false + */ + private boolean value; + + /** + * Constructs this number + * + * @param t the data + * @param excelFile the excel biff data + * @param v the value + * @param fr the formatting records + * @param es the external sheet + * @param nt the name table + * @param si the sheet + */ + public SharedBooleanFormulaRecord(Record t, + File excelFile, + boolean v, + FormattingRecords fr, + ExternalSheet es, + WorkbookMethods nt, + SheetImpl si) + { + super(t, fr, es, nt, si, excelFile.getPos()); + value = v; + } + + /** + * Interface method which Gets the boolean value stored in this cell. If + * this cell contains an error, then returns FALSE. Always query this cell + * type using the accessor method isError() prior to calling this method + * + * @return TRUE if this cell contains TRUE, FALSE if it contains FALSE or + * an error code + */ + public boolean getValue() + { + return value; + } + + /** + * Returns the numerical value as a string + * + * @return The numerical value of the formula as a string + */ + public String getContents() + { + // return Boolean.toString(value) - only available in 1.4 or later + return (new Boolean(value)).toString(); + } + + /** + * Returns the cell type + * + * @return The cell type + */ + public CellType getType() + { + return CellType.BOOLEAN_FORMULA; + } + + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array. Used when copying spreadsheets + * + * @return the raw record data + * @exception FormulaException + */ + public byte[] getFormulaData() throws FormulaException + { + if (!getSheet().getWorkbookBof().isBiff8()) + { + throw new FormulaException(FormulaException.BIFF8_SUPPORTED); + } + + // Get the tokens, taking into account the mapping from shared + // formula specific values into normal values + FormulaParser fp = new FormulaParser + (getTokens(), this, + getExternalSheet(), getNameTable(), + getSheet().getWorkbook().getSettings()); + fp.parse(); + byte[] rpnTokens = fp.getBytes(); + + byte[] data = new byte[rpnTokens.length + 22]; + + // Set the standard info for this cell + IntegerHelper.getTwoBytes(getRow(), data, 0); + IntegerHelper.getTwoBytes(getColumn(), data, 2); + IntegerHelper.getTwoBytes(getXFIndex(), data, 4); + data[6] = (byte) 1; + data[8] = (byte) (value == true ? 1 : 0); + data[12] = (byte) 0xff; + data[13] = (byte) 0xff; + + // Now copy in the parsed tokens + System.arraycopy(rpnTokens, 0, data, 22, rpnTokens.length); + IntegerHelper.getTwoBytes(rpnTokens.length, data, 20); + + // Lop off the standard information + byte[] d = new byte[data.length - 6]; + System.arraycopy(data, 6, d, 0, data.length - 6); + + return d; + } +} Index: 3rdParty_sources/jexcelapi/jxl/read/biff/SharedDateFormulaRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/SharedDateFormulaRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/SharedDateFormulaRecord.java 17 Aug 2012 14:50:55 -0000 1.1 @@ -0,0 +1,194 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import java.text.DateFormat; +import java.util.Date; + + +import jxl.CellType; +import jxl.DateCell; +import jxl.DateFormulaCell; +import jxl.biff.DoubleHelper; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.IntegerHelper; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; + +/** + * A number formula record, manufactured out of the Shared Formula + * "optimization" + */ +public class SharedDateFormulaRecord extends BaseSharedFormulaRecord + implements DateCell, FormulaData, DateFormulaCell +{ + /** + * Re-use the date record to handle all the formatting information and + * date calculations + */ + private DateRecord dateRecord; + + /** + * The double value + */ + private double value; + + /** + * Constructs this number formula + * + * @param nfr the number formula records + * @param fr the formatting records + * @param nf flag indicating whether this uses the 1904 date system + * @param si the sheet + * @param pos the position + */ + public SharedDateFormulaRecord(SharedNumberFormulaRecord nfr, + FormattingRecords fr, + boolean nf, + SheetImpl si, + int pos) + { + super(nfr.getRecord(), + fr, + nfr.getExternalSheet(), + nfr.getNameTable(), + si, + pos); + dateRecord = new DateRecord(nfr, nfr.getXFIndex(), fr, nf, si); + value = nfr.getValue(); + } + + /** + * Accessor for the value + * + * @return the value + */ + public double getValue() + { + return value; + } + + /** + * Accessor for the contents as a string + * + * @return the value as a string + */ + public String getContents() + { + return dateRecord.getContents(); + } + + /** + * Accessor for the cell type + * + * @return the cell type + */ + public CellType getType() + { + return CellType.DATE_FORMULA; + } + + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array. Used when copying spreadsheets + * + * @return the raw record data + * @exception FormulaException + */ + public byte[] getFormulaData() throws FormulaException + { + if (!getSheet().getWorkbookBof().isBiff8()) + { + throw new FormulaException(FormulaException.BIFF8_SUPPORTED); + } + + // Get the tokens, taking into account the mapping from shared + // formula specific values into normal values + FormulaParser fp = new FormulaParser + (getTokens(), this, + getExternalSheet(), getNameTable(), + getSheet().getWorkbook().getSettings()); + fp.parse(); + byte[] rpnTokens = fp.getBytes(); + + byte[] data = new byte[rpnTokens.length + 22]; + + // Set the standard info for this cell + IntegerHelper.getTwoBytes(getRow(), data, 0); + IntegerHelper.getTwoBytes(getColumn(), data, 2); + IntegerHelper.getTwoBytes(getXFIndex(), data, 4); + DoubleHelper.getIEEEBytes(value, data, 6); + + // Now copy in the parsed tokens + System.arraycopy(rpnTokens, 0, data, 22, rpnTokens.length); + IntegerHelper.getTwoBytes(rpnTokens.length, data, 20); + + // Lop off the standard information + byte[] d = new byte[data.length - 6]; + System.arraycopy(data, 6, d, 0, data.length - 6); + + return d; + } + + /** + * Gets the date + * + * @return the date + */ + public Date getDate() + { + return dateRecord.getDate(); + } + + /** + * Indicates whether the date value contained in this cell refers to a date, + * or merely a time + * + * @return TRUE if the value refers to a time + */ + public boolean isTime() + { + return dateRecord.isTime(); + } + + /** + * Gets the DateFormat used to format the cell. This will normally be + * the format specified in the excel spreadsheet, but in the event of any + * difficulty parsing this, it will revert to the default date/time format. + * + * @return the DateFormat object used to format the date in the original + * excel cell + */ + public DateFormat getDateFormat() + { + return dateRecord.getDateFormat(); + } + +} + + + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/SharedErrorFormulaRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/SharedErrorFormulaRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/SharedErrorFormulaRecord.java 17 Aug 2012 14:50:56 -0000 1.1 @@ -0,0 +1,177 @@ +/********************************************************************* +* +* Copyright (C) 2005 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import common.Logger; + +import jxl.CellType; +import jxl.ErrorCell; +import jxl.ErrorFormulaCell; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.IntegerHelper; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaErrorCode; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; + +/** + * A number formula record, manufactured out of the Shared Formula + * "optimization" + */ +public class SharedErrorFormulaRecord extends BaseSharedFormulaRecord + implements ErrorCell, FormulaData, ErrorFormulaCell +{ + /** + * The logger + */ + private static Logger logger = + Logger.getLogger(SharedErrorFormulaRecord.class); + + /** + * The error code of this cell + */ + private int errorCode; + + /** + * The raw data + */ + private byte[] data; + + /** + * The error code + */ + private FormulaErrorCode error; + + /** + * Constructs this number + * + * @param t the data + * @param excelFile the excel biff data + * @param v the errorCode + * @param fr the formatting records + * @param es the external sheet + * @param nt the name table + * @param si the sheet + */ + public SharedErrorFormulaRecord(Record t, + File excelFile, + int ec, + FormattingRecords fr, + ExternalSheet es, + WorkbookMethods nt, + SheetImpl si) + { + super(t, fr, es, nt, si, excelFile.getPos()); + errorCode = ec; + } + + /** + * Interface method which gets the error code for this cell. If this cell + * does not represent an error, then it returns 0. Always use the + * method isError() to determine this prior to calling this method + * + * @return the error code if this cell contains an error, 0 otherwise + */ + public int getErrorCode() + { + return errorCode; + } + + /** + * Returns the numerical value as a string + * + * @return The numerical value of the formula as a string + */ + public String getContents() + { + if (error == null) + { + error = FormulaErrorCode.getErrorCode(errorCode); + } + + return error != FormulaErrorCode.UNKNOWN ? + error.getDescription() : "ERROR " + errorCode; + } + + /** + * Accessor for the cell type + * + * @return the cell type + */ + public CellType getType() + { + return CellType.FORMULA_ERROR; + } + + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array. Used when copying spreadsheets + * + * @return the raw record data + * @exception FormulaException + */ + public byte[] getFormulaData() throws FormulaException + { + if (!getSheet().getWorkbookBof().isBiff8()) + { + throw new FormulaException(FormulaException.BIFF8_SUPPORTED); + } + + // Get the tokens, taking into account the mapping from shared + // formula specific values into normal values + FormulaParser fp = new FormulaParser + (getTokens(), this, + getExternalSheet(), getNameTable(), + getSheet().getWorkbook().getSettings()); + fp.parse(); + byte[] rpnTokens = fp.getBytes(); + + byte[] data = new byte[rpnTokens.length + 22]; + + // Set the standard info for this cell + IntegerHelper.getTwoBytes(getRow(), data, 0); + IntegerHelper.getTwoBytes(getColumn(), data, 2); + IntegerHelper.getTwoBytes(getXFIndex(), data, 4); + + data[6] = (byte) 0x02; // indicates this cell is an error value + data[8] = (byte) errorCode; + data[12] = (byte) 0xff; + data[13] = (byte) 0xff; + + // Now copy in the parsed tokens + System.arraycopy(rpnTokens, 0, data, 22, rpnTokens.length); + IntegerHelper.getTwoBytes(rpnTokens.length, data, 20); + + // Lop off the standard information + byte[] d = new byte[data.length - 6]; + System.arraycopy(data, 6, d, 0, data.length - 6); + + return d; + } +} + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/SharedFormulaRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/SharedFormulaRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/SharedFormulaRecord.java 17 Aug 2012 14:50:57 -0000 1.1 @@ -0,0 +1,231 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import java.text.NumberFormat; +import java.util.ArrayList; + +import common.Logger; + +import jxl.Cell; +import jxl.CellType; +import jxl.biff.FormattingRecords; +import jxl.biff.IntegerHelper; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; + +/** + * A shared formula + */ +class SharedFormulaRecord +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(SharedFormulaRecord.class); + + /** + * The first row to which this shared formula applies + */ + private int firstRow; + + /** + * The last row to which this shared formula applies + */ + private int lastRow; + + /** + * The first column to which this shared formula applies + */ + private int firstCol; + + /** + * The last column to which this shared formula applies + */ + private int lastCol; + + /** + * The first (template) formula comprising this group + */ + private BaseSharedFormulaRecord templateFormula; + + /** + * The rest of the cells comprising this shared formula + */ + private ArrayList formulas; + + /** + * The token data + */ + private byte[] tokens; + + /** + * A handle to the external sheet + */ + private ExternalSheet externalSheet; + + /** + * A handle to the sheet + */ + private SheetImpl sheet; + + + /** + * Constructs this object from the raw data. Creates either a + * NumberFormulaRecord or a StringFormulaRecord depending on whether + * this formula represents a numerical calculation or not + * + * @param t the raw data + * @param fr the base shared formula + * @param es the workbook, which contains the external sheet references + * @param nt the workbook + * @param si the sheet + */ + public SharedFormulaRecord(Record t, BaseSharedFormulaRecord fr, + ExternalSheet es, WorkbookMethods nt, + SheetImpl si) + { + sheet = si; + byte[] data = t.getData(); + + firstRow = IntegerHelper.getInt(data[0], data[1]); + lastRow = IntegerHelper.getInt(data[2], data[3]); + firstCol = data[4] & 0xff; + lastCol = data[5] & 0xff; + + formulas = new ArrayList(); + + templateFormula = fr; + + tokens = new byte[data.length - 10]; + System.arraycopy(data, 10, tokens, 0, tokens.length); + } + + /** + * Adds this formula to the list of formulas, if it falls within + * the bounds + * + * @param fr the formula record to test for membership of this group + * @return TRUE if the formulas was added, FALSE otherwise + */ + public boolean add(BaseSharedFormulaRecord fr) + { + boolean added = false; + int r = fr.getRow(); + if (r >= firstRow && r <= lastRow) + { + int c = fr.getColumn(); + if (c >= firstCol && c <= lastCol) + { + formulas.add(fr); + added = true; + } + } + + + return added; + } + + /** + * Manufactures individual cell formulas out the whole shared formula + * debacle + * + * @param fr the formatting records + * @param nf flag indicating whether this uses the 1904 date system + * @return an array of formulas to be added to the sheet + */ + Cell[] getFormulas(FormattingRecords fr, boolean nf) + { + Cell[] sfs = new Cell[formulas.size() + 1]; + + // This can happen if there are many identical formulas in the + // sheet and excel has not sliced and diced them exclusively + if (templateFormula == null) + { + logger.warn("Shared formula template formula is null"); + return new Cell[0]; + } + + templateFormula.setTokens(tokens); + NumberFormat templateNumberFormat = null; + + // See if the template formula evaluates to date + if (templateFormula.getType() == CellType.NUMBER_FORMULA) + { + SharedNumberFormulaRecord snfr = (SharedNumberFormulaRecord) + templateFormula; + templateNumberFormat = snfr.getNumberFormat(); + + if (fr.isDate(templateFormula.getXFIndex())) + { + templateFormula = new SharedDateFormulaRecord(snfr, fr, nf, sheet, + snfr.getFilePos()); + templateFormula.setTokens(snfr.getTokens()); + } + } + + sfs[0] = templateFormula; + + BaseSharedFormulaRecord f = null; + + for (int i = 0; i < formulas.size(); i++) + { + f = (BaseSharedFormulaRecord) formulas.get(i); + + // See if the formula evaluates to date + if (f.getType() == CellType.NUMBER_FORMULA) + { + SharedNumberFormulaRecord snfr = (SharedNumberFormulaRecord) f; + + if (fr.isDate(f.getXFIndex())) + { + f = new SharedDateFormulaRecord(snfr, fr, nf, sheet, + snfr.getFilePos()); + } + else + { + ;// snfr.setNumberFormat(templateNumberFormat); + } + } + + f.setTokens(tokens); + sfs[i + 1] = f; + } + + return sfs; + } + + /** + * Accessor for the template formula. Called when a shared formula has, + * for some reason, specified an inappropriate range and it is necessary + * to retrieve the template from a previously available shared formula + */ + BaseSharedFormulaRecord getTemplateFormula() + { + return templateFormula; + } +} + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/SharedNumberFormulaRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/SharedNumberFormulaRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/SharedNumberFormulaRecord.java 17 Aug 2012 14:50:56 -0000 1.1 @@ -0,0 +1,201 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import java.text.DecimalFormat; +import java.text.NumberFormat; + +import common.Logger; + +import jxl.CellType; +import jxl.NumberCell; +import jxl.NumberFormulaCell; + +import jxl.biff.DoubleHelper; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.IntegerHelper; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; + +/** + * A number formula record, manufactured out of the Shared Formula + * "optimization" + */ +public class SharedNumberFormulaRecord extends BaseSharedFormulaRecord + implements NumberCell, FormulaData, NumberFormulaCell +{ + /** + * The logger + */ + private static Logger logger = + Logger.getLogger(SharedNumberFormulaRecord.class); + /** + * The value of this number + */ + private double value; + /** + * The cell format + */ + private NumberFormat format; + /** + * A handle to the formatting records + */ + private FormattingRecords formattingRecords; + + /** + * The string format for the double value + */ + private static DecimalFormat defaultFormat = new DecimalFormat("#.###"); + + /** + * Constructs this number + * + * @param t the data + * @param excelFile the excel biff data + * @param v the value + * @param fr the formatting records + * @param es the external sheet + * @param nt the name table + * @param si the sheet + */ + public SharedNumberFormulaRecord(Record t, + File excelFile, + double v, + FormattingRecords fr, + ExternalSheet es, + WorkbookMethods nt, + SheetImpl si) + { + super(t, fr, es, nt, si, excelFile.getPos()); + value = v; + format = defaultFormat; // format is set up later from the + // SharedFormulaRecord + } + + /** + * Sets the format for the number based on the Excel spreadsheets' format. + * This is called from SheetImpl when it has been definitely established + * that this cell is a number and not a date + * + * @param f the format + */ + final void setNumberFormat(NumberFormat f) + { + if (f != null) + { + format = f; + } + } + + /** + * Accessor for the value + * + * @return the value + */ + public double getValue() + { + return value; + } + + /** + * Accessor for the contents as a string + * + * @return the value as a string + */ + public String getContents() + { + return !Double.isNaN(value) ? format.format(value) : ""; + } + + /** + * Accessor for the cell type + * + * @return the cell type + */ + public CellType getType() + { + return CellType.NUMBER_FORMULA; + } + + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array. Used when copying spreadsheets + * + * @return the raw record data + * @exception FormulaException + */ + public byte[] getFormulaData() throws FormulaException + { + if (!getSheet().getWorkbookBof().isBiff8()) + { + throw new FormulaException(FormulaException.BIFF8_SUPPORTED); + } + + // Get the tokens, taking into account the mapping from shared + // formula specific values into normal values + FormulaParser fp = new FormulaParser + (getTokens(), this, + getExternalSheet(), getNameTable(), + getSheet().getWorkbook().getSettings()); + fp.parse(); + byte[] rpnTokens = fp.getBytes(); + + byte[] data = new byte[rpnTokens.length + 22]; + + // Set the standard info for this cell + IntegerHelper.getTwoBytes(getRow(), data, 0); + IntegerHelper.getTwoBytes(getColumn(), data, 2); + IntegerHelper.getTwoBytes(getXFIndex(), data, 4); + DoubleHelper.getIEEEBytes(value, data, 6); + + // Now copy in the parsed tokens + System.arraycopy(rpnTokens, 0, data, 22, rpnTokens.length); + IntegerHelper.getTwoBytes(rpnTokens.length, data, 20); + + // Lop off the standard information + byte[] d = new byte[data.length - 6]; + System.arraycopy(data, 6, d, 0, data.length - 6); + + return d; + } + + /** + * Gets the NumberFormat used to format this cell. This is the java + * equivalent of the Excel format + * + * @return the NumberFormat used to format the cell + */ + public NumberFormat getNumberFormat() + { + return format; + } +} + + + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/SharedStringFormulaRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/SharedStringFormulaRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/SharedStringFormulaRecord.java 17 Aug 2012 14:50:56 -0000 1.1 @@ -0,0 +1,260 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import common.Assert; +import common.Logger; + +import jxl.CellType; +import jxl.LabelCell; +import jxl.StringFormulaCell; +import jxl.WorkbookSettings; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.IntegerHelper; +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; + +/** + * A string formula record, manufactured out of the Shared Formula + * "optimization" + */ +public class SharedStringFormulaRecord extends BaseSharedFormulaRecord + implements LabelCell, FormulaData, StringFormulaCell +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger + (SharedStringFormulaRecord.class); + + /** + * The value of this string formula + */ + private String value; + + // Dummy value for overloading the constructor when the string evaluates + // to null + private static final class EmptyString {}; + protected static final EmptyString EMPTY_STRING = new EmptyString(); + + /** + * Constructs this string formula + * + * @param t the record + * @param excelFile the excel file + * @param fr the formatting record + * @param es the external sheet + * @param nt the workbook + * @param si the sheet + * @param ws the workbook settings + */ + public SharedStringFormulaRecord(Record t, + File excelFile, + FormattingRecords fr, + ExternalSheet es, + WorkbookMethods nt, + SheetImpl si, + WorkbookSettings ws) + { + super(t, fr, es, nt, si, excelFile.getPos()); + int pos = excelFile.getPos(); + + // Save the position in the excel file + int filepos = excelFile.getPos(); + + // Look for the string record in one of the records after the + // formula. Put a cap on it to prevent ednas + Record nextRecord = excelFile.next(); + int count = 0; + while (nextRecord.getType() != Type.STRING && count < 4) + { + nextRecord = excelFile.next(); + count++; + } + Assert.verify(count < 4, " @ " + pos); + + byte[] stringData = nextRecord.getData(); + + // Read in any continuation records + nextRecord = excelFile.peek(); + while (nextRecord.getType() == Type.CONTINUE) + { + nextRecord = excelFile.next(); // move the pointer within the data + byte[] d = new byte[stringData.length + nextRecord.getLength() - 1]; + System.arraycopy(stringData, 0, d, 0, stringData.length); + System.arraycopy(nextRecord.getData(), 1, d, + stringData.length, nextRecord.getLength() - 1); + stringData = d; + nextRecord = excelFile.peek(); + } + + int chars = IntegerHelper.getInt(stringData[0], stringData[1]); + + boolean unicode = false; + int startpos = 3; + if (stringData.length == chars + 2) + { + // String might only consist of a one byte length indicator, instead + // of the more normal 2 + startpos = 2; + unicode = false; + } + else if (stringData[2] == 0x1) + { + // unicode string, two byte length indicator + startpos = 3; + unicode = true; + } + else + { + // ascii string, two byte length indicator + startpos = 3; + unicode = false; + } + + if (!unicode) + { + value = StringHelper.getString(stringData, chars, startpos, ws); + } + else + { + value = StringHelper.getUnicodeString(stringData, chars, startpos); + } + + // Restore the position in the excel file, to enable the SHRFMLA + // record to be picked up + excelFile.setPos(filepos); + } + + /** + * Constructs this string formula + * + * @param t the record + * @param excelFile the excel file + * @param fr the formatting record + * @param es the external sheet + * @param nt the workbook + * @param si the sheet + * @param dummy the overload indicator + */ + public SharedStringFormulaRecord(Record t, + File excelFile, + FormattingRecords fr, + ExternalSheet es, + WorkbookMethods nt, + SheetImpl si, + EmptyString dummy) + { + super(t, fr, es, nt, si, excelFile.getPos()); + + value = ""; + } + + /** + * Accessor for the value + * + * @return the value + */ + public String getString() + { + return value; + } + + /** + * Accessor for the contents as a string + * + * @return the value as a string + */ + public String getContents() + { + return value; + } + + /** + * Accessor for the cell type + * + * @return the cell type + */ + public CellType getType() + { + return CellType.STRING_FORMULA; + } + + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array. Used when copying spreadsheets + * + * @return the raw record data + * @exception FormulaException + */ + public byte[] getFormulaData() throws FormulaException + { + if (!getSheet().getWorkbookBof().isBiff8()) + { + throw new FormulaException(FormulaException.BIFF8_SUPPORTED); + } + + // Get the tokens, taking into account the mapping from shared + // formula specific values into normal values + FormulaParser fp = new FormulaParser + (getTokens(), this, + getExternalSheet(), getNameTable(), + getSheet().getWorkbook().getSettings()); + fp.parse(); + byte[] rpnTokens = fp.getBytes(); + + byte[] data = new byte[rpnTokens.length + 22]; + + // Set the standard info for this cell + IntegerHelper.getTwoBytes(getRow(), data, 0); + IntegerHelper.getTwoBytes(getColumn(), data, 2); + IntegerHelper.getTwoBytes(getXFIndex(), data, 4); + + // Set the two most significant bytes of the value to be 0xff in + // order to identify this as a string + data[6] = 0; + data[12] = -1; + data[13] = -1; + + // Now copy in the parsed tokens + System.arraycopy(rpnTokens, 0, data, 22, rpnTokens.length); + IntegerHelper.getTwoBytes(rpnTokens.length, data, 20); + + // Lop off the standard information + byte[] d = new byte[data.length - 6]; + System.arraycopy(data, 6, d, 0, data.length - 6); + + return d; + } +} + + + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/SheetImpl.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/SheetImpl.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/SheetImpl.java 17 Aug 2012 14:50:57 -0000 1.1 @@ -0,0 +1,1253 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.regex.Pattern; + +import common.Logger; +import common.Assert; + +import jxl.Cell; +import jxl.CellType; +import jxl.CellView; +import jxl.Hyperlink; +import jxl.Image; +import jxl.LabelCell; +import jxl.Range; +import jxl.Sheet; +import jxl.SheetSettings; +import jxl.WorkbookSettings; +import jxl.biff.BuiltInName; +import jxl.biff.AutoFilter; +import jxl.biff.CellFinder; +import jxl.biff.CellReferenceHelper; +import jxl.biff.ConditionalFormat; +import jxl.biff.DataValidation; +import jxl.biff.EmptyCell; +import jxl.biff.FormattingRecords; +import jxl.biff.Type; +import jxl.biff.WorkspaceInformationRecord; +import jxl.biff.drawing.Chart; +import jxl.biff.drawing.Drawing; +import jxl.biff.drawing.DrawingData; +import jxl.biff.drawing.DrawingGroupObject; +import jxl.format.CellFormat; + +/** + * Represents a sheet within a workbook. Provides a handle to the individual + * cells, or lines of cells (grouped by Row or Column) + * In order to simplify this class due to code bloat, the actual reading + * logic has been delegated to the SheetReaderClass. This class' main + * responsibility is now to implement the API methods declared in the + * Sheet interface + */ +public class SheetImpl implements Sheet +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(SheetImpl.class); + + /** + * The excel file + */ + private File excelFile; + /** + * A handle to the shared string table + */ + private SSTRecord sharedStrings; + + /** + * A handle to the sheet BOF record, which indicates the stream type + */ + private BOFRecord sheetBof; + + /** + * A handle to the workbook BOF record, which indicates the stream type + */ + private BOFRecord workbookBof; + + /** + * A handle to the formatting records + */ + private FormattingRecords formattingRecords; + + /** + * The name of this sheet + */ + private String name; + + /** + * The number of rows + */ + private int numRows; + + /** + * The number of columns + */ + private int numCols; + + /** + * The cells + */ + private Cell[][] cells; + + /** + * The start position in the stream of this sheet + */ + private int startPosition; + + /** + * The list of specified (ie. non default) column widths + */ + private ColumnInfoRecord[] columnInfos; + + /** + * The array of row records + */ + private RowRecord[] rowRecords; + + /** + * The list of non-default row properties + */ + private ArrayList rowProperties; + + /** + * An array of column info records. They are held this way before + * they are transferred to the more convenient array + */ + private ArrayList columnInfosArray; + + /** + * A list of shared formula groups + */ + private ArrayList sharedFormulas; + + /** + * A list of hyperlinks on this page + */ + private ArrayList hyperlinks; + + /** + * A list of charts on this page + */ + private ArrayList charts; + + /** + * A list of drawings on this page + */ + private ArrayList drawings; + + /** + * A list of drawings (as opposed to comments/validation/charts) on this + * page + */ + private ArrayList images; + + /** + * A list of data validations on this page + */ + private DataValidation dataValidation; + + /** + * A list of merged cells on this page + */ + private Range[] mergedCells; + + /** + * Indicates whether the columnInfos array has been initialized + */ + private boolean columnInfosInitialized; + + /** + * Indicates whether the rowRecords array has been initialized + */ + private boolean rowRecordsInitialized; + + /** + * Indicates whether or not the dates are based around the 1904 date system + */ + private boolean nineteenFour; + + /** + * The workspace options + */ + private WorkspaceInformationRecord workspaceOptions; + + /** + * The hidden flag + */ + private boolean hidden; + + /** + * The environment specific print record + */ + private PLSRecord plsRecord; + + /** + * The property set record associated with this workbook + */ + private ButtonPropertySetRecord buttonPropertySet; + + /** + * The sheet settings + */ + private SheetSettings settings; + + /** + * The horizontal page breaks contained on this sheet + */ + private int[] rowBreaks; + + /** + * The vertical page breaks contained on this sheet + */ + private int[] columnBreaks; + + /** + * The maximum row outline level + */ + private int maxRowOutlineLevel; + + /** + * The maximum column outline level + */ + private int maxColumnOutlineLevel; + + /** + * The list of local names for this sheet + */ + private ArrayList localNames; + + /** + * The list of conditional formats for this sheet + */ + private ArrayList conditionalFormats; + + /** + * The autofilter information + */ + private AutoFilter autoFilter; + + /** + * A handle to the workbook which contains this sheet. Some of the records + * need this in order to reference external sheets + */ + private WorkbookParser workbook; + + /** + * A handle to the workbook settings + */ + private WorkbookSettings workbookSettings; + + /** + * Constructor + * + * @param f the excel file + * @param sst the shared string table + * @param fr formatting records + * @param sb the bof record which indicates the start of the sheet + * @param wb the bof record which indicates the start of the sheet + * @param nf the 1904 flag + * @param wp the workbook which this sheet belongs to + * @exception BiffException + */ + SheetImpl(File f, + SSTRecord sst, + FormattingRecords fr, + BOFRecord sb, + BOFRecord wb, + boolean nf, + WorkbookParser wp) + throws BiffException + { + excelFile = f; + sharedStrings = sst; + formattingRecords = fr; + sheetBof = sb; + workbookBof = wb; + columnInfosArray = new ArrayList(); + sharedFormulas = new ArrayList(); + hyperlinks = new ArrayList(); + rowProperties = new ArrayList(10); + columnInfosInitialized = false; + rowRecordsInitialized = false; + nineteenFour = nf; + workbook = wp; + workbookSettings = workbook.getSettings(); + + // Mark the position in the stream, and then skip on until the end + startPosition = f.getPos(); + + if (sheetBof.isChart()) + { + // Set the start pos to include the bof so the sheet reader can handle it + startPosition -= (sheetBof.getLength() + 4); + } + + Record r = null; + int bofs = 1; + + while (bofs >= 1) + { + r = f.next(); + + // use this form for quick performance + if (r.getCode() == Type.EOF.value) + { + bofs--; + } + + if (r.getCode() == Type.BOF.value) + { + bofs++; + } + } + } + + /** + * Returns the cell for the specified location eg. "A4", using the + * CellReferenceHelper + * + * @param loc the cell reference + * @return the cell at the specified co-ordinates + */ + public Cell getCell(String loc) + { + return getCell(CellReferenceHelper.getColumn(loc), + CellReferenceHelper.getRow(loc)); + } + + /** + * Returns the cell specified at this row and at this column + * + * @param row the row number + * @param column the column number + * @return the cell at the specified co-ordinates + */ + public Cell getCell(int column, int row) + { + // just in case this has been cleared, but something else holds + // a reference to it + if (cells == null) + { + readSheet(); + } + + Cell c = cells[row][column]; + + if (c == null) + { + c = new EmptyCell(column, row); + cells[row][column] = c; + } + + return c; + } + + /** + * Gets the cell whose contents match the string passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform + * + * @param contents the string to match + * @return the Cell whose contents match the paramter, null if not found + */ + public Cell findCell(String contents) + { + CellFinder cellFinder = new CellFinder(this); + return cellFinder.findCell(contents); + } + + /** + * Gets the cell whose contents match the string passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform + * + * @param contents the string to match + * @param firstCol the first column within the range + * @param firstRow the first row of the range + * @param lastCol the last column within the range + * @param lastRow the last row within the range + * @param reverse indicates whether to perform a reverse search or not + * @return the Cell whose contents match the parameter, null if not found + */ + public Cell findCell(String contents, + int firstCol, + int firstRow, + int lastCol, + int lastRow, + boolean reverse) + { + CellFinder cellFinder = new CellFinder(this); + return cellFinder.findCell(contents, + firstCol, + firstRow, + lastCol, + lastRow, + reverse); + } + + /** + * Gets the cell whose contents match the regular expressionstring passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform + * + * @param pattern the regular expression string to match + * @param firstCol the first column within the range + * @param firstRow the first row of the range + * @param lastRow the last row within the range + * @param lastCol the last column within the ranage + * @param reverse indicates whether to perform a reverse search or not + * @return the Cell whose contents match the parameter, null if not found + */ + public Cell findCell(Pattern pattern, + int firstCol, + int firstRow, + int lastCol, + int lastRow, + boolean reverse) + { + CellFinder cellFinder = new CellFinder(this); + return cellFinder.findCell(pattern, + firstCol, + firstRow, + lastCol, + lastRow, + reverse); + } + + /** + * Gets the cell whose contents match the string passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform. This method differs + * from the findCell methods in that only cells with labels are + * queried - all numerical cells are ignored. This should therefore + * improve performance. + * + * @param contents the string to match + * @return the Cell whose contents match the paramter, null if not found + */ + public LabelCell findLabelCell(String contents) + { + CellFinder cellFinder = new CellFinder(this); + return cellFinder.findLabelCell(contents); + } + + /** + * Returns the number of rows in this sheet + * + * @return the number of rows in this sheet + */ + public int getRows() + { + // just in case this has been cleared, but something else holds + // a reference to it + if (cells == null) + { + readSheet(); + } + + return numRows; + } + + /** + * Returns the number of columns in this sheet + * + * @return the number of columns in this sheet + */ + public int getColumns() + { + // just in case this has been cleared, but something else holds + // a reference to it + if (cells == null) + { + readSheet(); + } + + return numCols; + } + + /** + * Gets all the cells on the specified row. The returned array will + * be stripped of all trailing empty cells + * + * @param row the rows whose cells are to be returned + * @return the cells on the given row + */ + public Cell[] getRow(int row) + { + // just in case this has been cleared, but something else holds + // a reference to it + if (cells == null) + { + readSheet(); + } + + // Find the last non-null cell + boolean found = false; + int col = numCols - 1; + while (col >= 0 && !found) + { + if (cells[row][col] != null) + { + found = true; + } + else + { + col--; + } + } + + // Only create entries for non-null cells + Cell[] c = new Cell[col + 1]; + + for (int i = 0; i <= col; i++) + { + c[i] = getCell(i, row); + } + return c; + } + + /** + * Gets all the cells on the specified column. The returned array + * will be stripped of all trailing empty cells + * + * @param col the column whose cells are to be returned + * @return the cells on the specified column + */ + public Cell[] getColumn(int col) + { + // just in case this has been cleared, but something else holds + // a reference to it + if (cells == null) + { + readSheet(); + } + + // Find the last non-null cell + boolean found = false; + int row = numRows - 1; + while (row >= 0 && !found) + { + if (cells[row][col] != null) + { + found = true; + } + else + { + row--; + } + } + + // Only create entries for non-null cells + Cell[] c = new Cell[row + 1]; + + for (int i = 0; i <= row; i++) + { + c[i] = getCell(col, i); + } + return c; + } + + /** + * Gets the name of this sheet + * + * @return the name of the sheet + */ + public String getName() + { + return name; + } + + /** + * Sets the name of this sheet + * + * @param s the sheet name + */ + final void setName(String s) + { + name = s; + } + + /** + * Determines whether the sheet is hidden + * + * @return whether or not the sheet is hidden + * @deprecated in favour of the getSettings function + */ + public boolean isHidden() + { + return hidden; + } + + /** + * Gets the column info record for the specified column. If no + * column is specified, null is returned + * + * @param col the column + * @return the ColumnInfoRecord if specified, NULL otherwise + */ + public ColumnInfoRecord getColumnInfo(int col) + { + if (!columnInfosInitialized) + { + // Initialize the array + Iterator i = columnInfosArray.iterator(); + ColumnInfoRecord cir = null; + while (i.hasNext()) + { + cir = (ColumnInfoRecord) i.next(); + + int startcol = Math.max(0, cir.getStartColumn()); + int endcol = Math.min(columnInfos.length - 1, cir.getEndColumn()); + + for (int c = startcol; c <= endcol; c++) + { + columnInfos[c] = cir; + } + + if (endcol < startcol) + { + columnInfos[startcol] = cir; + } + } + + columnInfosInitialized = true; + } + + return col < columnInfos.length ? columnInfos[col] : null; + } + + /** + * Gets all the column info records + * + * @return the ColumnInfoRecordArray + */ + public ColumnInfoRecord[] getColumnInfos() + { + // Just chuck all the column infos we have into an array + ColumnInfoRecord[] infos = new ColumnInfoRecord[columnInfosArray.size()]; + for (int i = 0; i < columnInfosArray.size(); i++) + { + infos[i] = (ColumnInfoRecord) columnInfosArray.get(i); + } + + return infos; + } + + /** + * Sets the visibility of this sheet + * + * @param h hidden flag + */ + final void setHidden(boolean h) + { + hidden = h; + } + + /** + * Clears out the array of cells. This is done for memory allocation + * reasons when reading very large sheets + */ + final void clear() + { + cells = null; + mergedCells = null; + columnInfosArray.clear(); + sharedFormulas.clear(); + hyperlinks.clear(); + columnInfosInitialized = false; + + if (!workbookSettings.getGCDisabled()) + { + System.gc(); + } + } + + /** + * Reads in the contents of this sheet + */ + final void readSheet() + { + // If this sheet contains only a chart, then set everything to + // empty and do not bother parsing the sheet + // Thanks to steve.brophy for spotting this + if (!sheetBof.isWorksheet()) + { + numRows = 0; + numCols = 0; + cells = new Cell[0][0]; + // return; + } + + SheetReader reader = new SheetReader(excelFile, + sharedStrings, + formattingRecords, + sheetBof, + workbookBof, + nineteenFour, + workbook, + startPosition, + this); + reader.read(); + + // Take stuff that was read in + numRows = reader.getNumRows(); + numCols = reader.getNumCols(); + cells = reader.getCells(); + rowProperties = reader.getRowProperties(); + columnInfosArray = reader.getColumnInfosArray(); + hyperlinks = reader.getHyperlinks(); + conditionalFormats = reader.getConditionalFormats(); + autoFilter = reader.getAutoFilter(); + charts = reader.getCharts(); + drawings = reader.getDrawings(); + dataValidation = reader.getDataValidation(); + mergedCells = reader.getMergedCells(); + settings = reader.getSettings(); + settings.setHidden(hidden); + rowBreaks = reader.getRowBreaks(); + columnBreaks = reader.getColumnBreaks(); + workspaceOptions = reader.getWorkspaceOptions(); + plsRecord = reader.getPLS(); + buttonPropertySet = reader.getButtonPropertySet(); + maxRowOutlineLevel = reader.getMaxRowOutlineLevel(); + maxColumnOutlineLevel = reader.getMaxColumnOutlineLevel(); + + reader = null; + + if (!workbookSettings.getGCDisabled()) + { + System.gc(); + } + + if (columnInfosArray.size() > 0) + { + ColumnInfoRecord cir = (ColumnInfoRecord) + columnInfosArray.get(columnInfosArray.size() - 1); + columnInfos = new ColumnInfoRecord[cir.getEndColumn() + 1]; + } + else + { + columnInfos = new ColumnInfoRecord[0]; + } + + // Add any local names + if (localNames != null) + { + for (Iterator it = localNames.iterator(); it.hasNext() ;) + { + NameRecord nr = (NameRecord) it.next(); + if (nr.getBuiltInName() == BuiltInName.PRINT_AREA) + { + if(nr.getRanges().length > 0) + { + NameRecord.NameRange rng = nr.getRanges()[0]; + settings.setPrintArea(rng.getFirstColumn(), + rng.getFirstRow(), + rng.getLastColumn(), + rng.getLastRow()); + } + } + else if (nr.getBuiltInName() == BuiltInName.PRINT_TITLES) + { + // There can be 1 or 2 entries. + // Row entries have hardwired column entries (first and last + // possible column) + // Column entries have hardwired row entries (first and last + // possible row) + for (int i = 0 ; i < nr.getRanges().length ; i++) + { + NameRecord.NameRange rng = nr.getRanges()[i]; + if (rng.getFirstColumn() == 0 && rng.getLastColumn() == 255) + { + settings.setPrintTitlesRow(rng.getFirstRow(), + rng.getLastRow()); + } + else + { + settings.setPrintTitlesCol(rng.getFirstColumn(), + rng.getLastColumn()); + } + } + } + } + } + } + + /** + * Gets the hyperlinks on this sheet + * + * @return an array of hyperlinks + */ + public Hyperlink[] getHyperlinks() + { + Hyperlink[] hl = new Hyperlink[hyperlinks.size()]; + + for (int i = 0; i < hyperlinks.size(); i++) + { + hl[i] = (Hyperlink) hyperlinks.get(i); + } + + return hl; + } + + /** + * Gets the cells which have been merged on this sheet + * + * @return an array of range objects + */ + public Range[] getMergedCells() + { + if (mergedCells == null) + { + return new Range[0]; + } + + return mergedCells; + } + + /** + * Gets the non-default rows. Used when copying spreadsheets + * + * @return an array of row properties + */ + public RowRecord[] getRowProperties() + { + RowRecord[] rp = new RowRecord[rowProperties.size()]; + for (int i = 0; i < rp.length; i++) + { + rp[i] = (RowRecord) rowProperties.get(i); + } + + return rp; + } + + /** + * Gets the data validations. Used when copying sheets + * + * @return the data validations + */ + public DataValidation getDataValidation() + { + return dataValidation; + } + + /** + * Gets the row record. Usually called by the cell in the specified + * row in order to determine its size + * + * @param r the row + * @return the RowRecord for the specified row + */ + RowRecord getRowInfo(int r) + { + if (!rowRecordsInitialized) + { + rowRecords = new RowRecord[getRows()]; + Iterator i = rowProperties.iterator(); + + int rownum = 0; + RowRecord rr = null; + while (i.hasNext()) + { + rr = (RowRecord) i.next(); + rownum = rr.getRowNumber(); + if (rownum < rowRecords.length) + { + rowRecords[rownum] = rr; + } + } + + rowRecordsInitialized = true; + } + + return r < rowRecords.length ? rowRecords[r] : null; + } + + /** + * Gets the row breaks. Called when copying sheets + * + * @return the explicit row breaks + */ + public final int[] getRowPageBreaks() + { + return rowBreaks; + } + + /** + * Gets the row breaks. Called when copying sheets + * + * @return the explicit row breaks + */ + public final int[] getColumnPageBreaks() + { + return columnBreaks; + } + + /** + * Gets the charts. Called when copying sheets + * + * @return the charts on this page + */ + public final Chart[] getCharts() + { + Chart[] ch = new Chart[charts.size()]; + + for (int i = 0; i < ch.length; i++) + { + ch[i] = (Chart) charts.get(i); + } + return ch; + } + + /** + * Gets the drawings. Called when copying sheets + * + * @return the drawings on this page + */ + public final DrawingGroupObject[] getDrawings() + { + DrawingGroupObject[] dr = new DrawingGroupObject[drawings.size()]; + dr = (DrawingGroupObject[]) drawings.toArray(dr); + return dr; + } + + /** + * Determines whether the sheet is protected + * + * @return whether or not the sheet is protected + * @deprecated in favour of the getSettings() api + */ + public boolean isProtected() + { + return settings.isProtected(); + } + + /** + * Gets the workspace options for this sheet. Called during the copy + * process + * + * @return the workspace options + */ + public WorkspaceInformationRecord getWorkspaceOptions() + { + return workspaceOptions; + } + + /** + * Accessor for the sheet settings + * + * @return the settings for this sheet + */ + public SheetSettings getSettings() + { + return settings; + } + + /** + * Accessor for the workbook. In addition to be being used by this package, + * it is also used during the importSheet process + * + * @return the workbook + */ + public WorkbookParser getWorkbook() + { + return workbook; + } + + /** + * Gets the column format for the specified column + * + * @param col the column number + * @return the column format, or NULL if the column has no specific format + * @deprecated use getColumnView instead + */ + public CellFormat getColumnFormat(int col) + { + CellView cv = getColumnView(col); + return cv.getFormat(); + } + + /** + * Gets the column width for the specified column + * + * @param col the column number + * @return the column width, or the default width if the column has no + * specified format + */ + public int getColumnWidth(int col) + { + return getColumnView(col).getSize() / 256; + } + + /** + * Gets the column width for the specified column + * + * @param col the column number + * @return the column format, or the default format if no override is + specified + */ + public CellView getColumnView(int col) + { + ColumnInfoRecord cir = getColumnInfo(col); + CellView cv = new CellView(); + + if (cir != null) + { + cv.setDimension(cir.getWidth() / 256); //deprecated + cv.setSize(cir.getWidth()); + cv.setHidden(cir.getHidden()); + cv.setFormat(formattingRecords.getXFRecord(cir.getXFIndex())); + } + else + { + cv.setDimension(settings.getDefaultColumnWidth()); //deprecated + cv.setSize(settings.getDefaultColumnWidth() * 256); + } + + return cv; + } + + /** + * Gets the row height for the specified column + * + * @param row the row number + * @return the row height, or the default height if the row has no + * specified format + * @deprecated use getRowView instead + */ + public int getRowHeight(int row) + { + return getRowView(row).getDimension(); + } + + /** + * Gets the row view for the specified row + * + * @param row the row number + * @return the row format, or the default format if no override is + specified + */ + public CellView getRowView(int row) + { + RowRecord rr = getRowInfo(row); + + CellView cv = new CellView(); + + if (rr != null) + { + cv.setDimension(rr.getRowHeight()); //deprecated + cv.setSize(rr.getRowHeight()); + cv.setHidden(rr.isCollapsed()); + if (rr.hasDefaultFormat()) + { + cv.setFormat(formattingRecords.getXFRecord(rr.getXFIndex())); + } + } + else + { + cv.setDimension(settings.getDefaultRowHeight()); + cv.setSize(settings.getDefaultRowHeight()); //deprecated + } + + return cv; + } + + + /** + * Used when copying sheets in order to determine the type of this sheet + * + * @return the BOF Record + */ + public BOFRecord getSheetBof() + { + return sheetBof; + } + + /** + * Used when copying sheets in order to determine the type of the containing + * workboook + * + * @return the workbook BOF Record + */ + public BOFRecord getWorkbookBof() + { + return workbookBof; + } + + /** + * Accessor for the environment specific print record, invoked when + * copying sheets + * + * @return the environment specific print record + */ + public PLSRecord getPLS() + { + return plsRecord; + } + + /** + * Accessor for the button property set, used during copying + * + * @return the button property set + */ + public ButtonPropertySetRecord getButtonPropertySet() + { + return buttonPropertySet; + } + + /** + * Accessor for the number of images on the sheet + * + * @return the number of images on this sheet + */ + public int getNumberOfImages() + { + if (images == null) + { + initializeImages(); + } + + return images.size(); + } + + /** + * Accessor for the image + * + * @param i the 0 based image number + * @return the image at the specified position + */ + public Image getDrawing(int i) + { + if (images == null) + { + initializeImages(); + } + + return (Image) images.get(i); + } + + /** + * Initializes the images + */ + private void initializeImages() + { + if (images != null) + { + return; + } + + images = new ArrayList(); + DrawingGroupObject[] dgos = getDrawings(); + + for (int i = 0; i < dgos.length; i++) + { + if (dgos[i] instanceof Drawing) + { + images.add(dgos[i]); + } + } + } + + /** + * Used by one of the demo programs for debugging purposes only + */ + public DrawingData getDrawingData() + { + SheetReader reader = new SheetReader(excelFile, + sharedStrings, + formattingRecords, + sheetBof, + workbookBof, + nineteenFour, + workbook, + startPosition, + this); + reader.read(); + return reader.getDrawingData(); + } + + /** + * Adds a local name to this shate + * + * @param nr the local name to add + */ + void addLocalName(NameRecord nr) + { + if (localNames == null) + { + localNames = new ArrayList(); + } + + localNames.add(nr); + } + + /** + * Gets the conditional formats + * + * @return the conditional formats + */ + public ConditionalFormat[] getConditionalFormats() + { + ConditionalFormat[] formats = + new ConditionalFormat[conditionalFormats.size()]; + formats = (ConditionalFormat[]) conditionalFormats.toArray(formats); + return formats; + } + + /** + * Returns the autofilter + * + * @return the autofilter + */ + public AutoFilter getAutoFilter() + { + return autoFilter; + } + + /** + * Accessor for the maximum column outline level. Used during a copy + * + * @return the maximum column outline level, or 0 if no outlines/groups + */ + public int getMaxColumnOutlineLevel() + { + return maxColumnOutlineLevel; + } + + /** + * Accessor for the maximum row outline level. Used during a copy + * + * @return the maximum row outline level, or 0 if no outlines/groups + */ + public int getMaxRowOutlineLevel() + { + return maxRowOutlineLevel; + } + +} Index: 3rdParty_sources/jexcelapi/jxl/read/biff/SheetReader.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/SheetReader.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/SheetReader.java 17 Aug 2012 14:50:57 -0000 1.1 @@ -0,0 +1,1849 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; + +import common.Assert; +import common.Logger; + +import jxl.Cell; +import jxl.CellFeatures; +import jxl.CellReferenceHelper; +import jxl.CellType; +import jxl.DateCell; +import jxl.HeaderFooter; +import jxl.Range; +import jxl.SheetSettings; +import jxl.WorkbookSettings; +import jxl.biff.AutoFilter; +import jxl.biff.AutoFilterInfoRecord; +import jxl.biff.AutoFilterRecord; +import jxl.biff.ConditionalFormat; +import jxl.biff.ConditionalFormatRangeRecord; +import jxl.biff.ConditionalFormatRecord; +import jxl.biff.ContinueRecord; +import jxl.biff.DataValidation; +import jxl.biff.DataValidityListRecord; +import jxl.biff.DataValiditySettingsRecord; +import jxl.biff.FilterModeRecord; +import jxl.biff.FormattingRecords; +import jxl.biff.Type; +import jxl.biff.WorkspaceInformationRecord; +import jxl.biff.drawing.Button; +import jxl.biff.drawing.Chart; +import jxl.biff.drawing.ComboBox; +import jxl.biff.drawing.Comment; +import jxl.biff.drawing.Drawing; +import jxl.biff.drawing.Drawing2; +import jxl.biff.drawing.DrawingData; +import jxl.biff.drawing.DrawingDataException; +import jxl.biff.drawing.MsoDrawingRecord; +import jxl.biff.drawing.NoteRecord; +import jxl.biff.drawing.ObjRecord; +import jxl.biff.drawing.TextObjectRecord; +import jxl.biff.formula.FormulaException; +import jxl.format.PageOrder; +import jxl.format.PageOrientation; +import jxl.format.PaperSize; + +/** + * Reads the sheet. This functionality was originally part of the + * SheetImpl class, but was separated out in order to simplify the former + * class + */ +final class SheetReader +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(SheetReader.class); + + /** + * The excel file + */ + private File excelFile; + + /** + * A handle to the shared string table + */ + private SSTRecord sharedStrings; + + /** + * A handle to the sheet BOF record, which indicates the stream type + */ + private BOFRecord sheetBof; + + /** + * A handle to the workbook BOF record, which indicates the stream type + */ + private BOFRecord workbookBof; + + /** + * A handle to the formatting records + */ + private FormattingRecords formattingRecords; + + /** + * The number of rows + */ + private int numRows; + + /** + * The number of columns + */ + private int numCols; + + /** + * The cells + */ + private Cell[][] cells; + + /** + * Any cells which are out of the defined bounds + */ + private ArrayList outOfBoundsCells; + + /** + * The start position in the stream of this sheet + */ + private int startPosition; + + /** + * The list of non-default row properties + */ + private ArrayList rowProperties; + + /** + * An array of column info records. They are held this way before + * they are transferred to the more convenient array + */ + private ArrayList columnInfosArray; + + /** + * A list of shared formula groups + */ + private ArrayList sharedFormulas; + + /** + * A list of hyperlinks on this page + */ + private ArrayList hyperlinks; + + /** + * The list of conditional formats on this page + */ + private ArrayList conditionalFormats; + + /** + * The autofilter information + */ + private AutoFilter autoFilter; + + /** + * A list of merged cells on this page + */ + private Range[] mergedCells; + + /** + * The list of data validations on this page + */ + private DataValidation dataValidation; + + /** + * The list of charts on this page + */ + private ArrayList charts; + + /** + * The list of drawings on this page + */ + private ArrayList drawings; + + /** + * The drawing data for the drawings + */ + private DrawingData drawingData; + + /** + * Indicates whether or not the dates are based around the 1904 date system + */ + private boolean nineteenFour; + + /** + * The PLS print record + */ + private PLSRecord plsRecord; + + /** + * The property set record associated with this workbook + */ + private ButtonPropertySetRecord buttonPropertySet; + + /** + * The workspace options + */ + private WorkspaceInformationRecord workspaceOptions; + + /** + * The horizontal page breaks contained on this sheet + */ + private int[] rowBreaks; + + /** + * The vertical page breaks contained on this sheet + */ + private int[] columnBreaks; + + /** + * The maximum row outline level + */ + private int maxRowOutlineLevel; + + /** + * The maximum column outline level + */ + private int maxColumnOutlineLevel; + + /** + * The sheet settings + */ + private SheetSettings settings; + + /** + * The workbook settings + */ + private WorkbookSettings workbookSettings; + + /** + * A handle to the workbook which contains this sheet. Some of the records + * need this in order to reference external sheets + */ + private WorkbookParser workbook; + + /** + * A handle to the sheet + */ + private SheetImpl sheet; + + /** + * Constructor + * + * @param fr the formatting records + * @param sst the shared string table + * @param f the excel file + * @param sb the bof record which indicates the start of the sheet + * @param wb the bof record which indicates the start of the sheet + * @param wp the workbook which this sheet belongs to + * @param sp the start position of the sheet bof in the excel file + * @param sh the sheet + * @param nf 1904 date record flag + * @exception BiffException + */ + SheetReader(File f, + SSTRecord sst, + FormattingRecords fr, + BOFRecord sb, + BOFRecord wb, + boolean nf, + WorkbookParser wp, + int sp, + SheetImpl sh) + { + excelFile = f; + sharedStrings = sst; + formattingRecords = fr; + sheetBof = sb; + workbookBof = wb; + columnInfosArray = new ArrayList(); + sharedFormulas = new ArrayList(); + hyperlinks = new ArrayList(); + conditionalFormats = new ArrayList(); + rowProperties = new ArrayList(10); + charts = new ArrayList(); + drawings = new ArrayList(); + outOfBoundsCells = new ArrayList(); + nineteenFour = nf; + workbook = wp; + startPosition = sp; + sheet = sh; + settings = new SheetSettings(sh); + workbookSettings = workbook.getSettings(); + } + + /** + * Adds the cell to the array + * + * @param cell the cell to add + */ + private void addCell(Cell cell) + { + // Sometimes multiple cells (eg. MULBLANK) can exceed the + // column/row boundaries. Ignore these + if (cell.getRow() < numRows && cell.getColumn() < numCols) + { + if (cells[cell.getRow()][cell.getColumn()] != null) + { + StringBuffer sb = new StringBuffer(); + CellReferenceHelper.getCellReference + (cell.getColumn(), cell.getRow(), sb); + logger.warn("Cell " + sb.toString() + + " already contains data"); + } + cells[cell.getRow()][cell.getColumn()] = cell; + } + else + { + outOfBoundsCells.add(cell); + /* + logger.warn("Cell " + + CellReferenceHelper.getCellReference + (cell.getColumn(), cell.getRow()) + + " exceeds defined cell boundaries in Dimension record " + + "(" + numCols + "x" + numRows + ")"); + */ + } + } + + /** + * Reads in the contents of this sheet + */ + final void read() + { + Record r = null; + BaseSharedFormulaRecord sharedFormula = null; + boolean sharedFormulaAdded = false; + + boolean cont = true; + + // Set the position within the file + excelFile.setPos(startPosition); + + // Handles to the last drawing and obj records + MsoDrawingRecord msoRecord = null; + ObjRecord objRecord = null; + boolean firstMsoRecord = true; + + // Handle to the last conditional format record + ConditionalFormat condFormat = null; + + // Handle to the autofilter records + FilterModeRecord filterMode = null; + AutoFilterInfoRecord autoFilterInfo = null; + + // A handle to window2 record + Window2Record window2Record = null; + + // A handle to printgridlines record + PrintGridLinesRecord printGridLinesRecord = null; + + // A handle to printheaders record + PrintHeadersRecord printHeadersRecord = null; + + // Hash map of comments, indexed on objectId. As each corresponding + // note record is encountered, these are removed from the array + HashMap comments = new HashMap(); + + // A list of object ids - used for cross referencing + ArrayList objectIds = new ArrayList(); + + // A handle to a continue record read in + ContinueRecord continueRecord = null; + + while (cont) + { + r = excelFile.next(); + Type type = r.getType(); + + if (type == Type.UNKNOWN && r.getCode() == 0) + { + logger.warn("Biff code zero found"); + + // Try a dimension record + if (r.getLength() == 0xa) + { + logger.warn("Biff code zero found - trying a dimension record."); + r.setType(Type.DIMENSION); + } + else + { + logger.warn("Biff code zero found - Ignoring."); + } + } + + if (type == Type.DIMENSION) + { + DimensionRecord dr = null; + + if (workbookBof.isBiff8()) + { + dr = new DimensionRecord(r); + } + else + { + dr = new DimensionRecord(r, DimensionRecord.biff7); + } + numRows = dr.getNumberOfRows(); + numCols = dr.getNumberOfColumns(); + cells = new Cell[numRows][numCols]; + } + else if (type == Type.LABELSST) + { + LabelSSTRecord label = new LabelSSTRecord(r, + sharedStrings, + formattingRecords, + sheet); + addCell(label); + } + else if (type == Type.RK || type == Type.RK2) + { + RKRecord rkr = new RKRecord(r, formattingRecords, sheet); + + if (formattingRecords.isDate(rkr.getXFIndex())) + { + DateCell dc = new DateRecord + (rkr, rkr.getXFIndex(), formattingRecords, nineteenFour, sheet); + addCell(dc); + } + else + { + addCell(rkr); + } + } + else if (type == Type.HLINK) + { + HyperlinkRecord hr = new HyperlinkRecord(r, sheet, workbookSettings); + hyperlinks.add(hr); + } + else if (type == Type.MERGEDCELLS) + { + MergedCellsRecord mc = new MergedCellsRecord(r, sheet); + if (mergedCells == null) + { + mergedCells = mc.getRanges(); + } + else + { + Range[] newMergedCells = + new Range[mergedCells.length + mc.getRanges().length]; + System.arraycopy(mergedCells, 0, newMergedCells, 0, + mergedCells.length); + System.arraycopy(mc.getRanges(), + 0, + newMergedCells, mergedCells.length, + mc.getRanges().length); + mergedCells = newMergedCells; + } + } + else if (type == Type.MULRK) + { + MulRKRecord mulrk = new MulRKRecord(r); + + // Get the individual cell records from the multiple record + int num = mulrk.getNumberOfColumns(); + int ixf = 0; + for (int i = 0; i < num; i++) + { + ixf = mulrk.getXFIndex(i); + + NumberValue nv = new NumberValue + (mulrk.getRow(), + mulrk.getFirstColumn() + i, + RKHelper.getDouble(mulrk.getRKNumber(i)), + ixf, + formattingRecords, + sheet); + + + if (formattingRecords.isDate(ixf)) + { + DateCell dc = new DateRecord(nv, + ixf, + formattingRecords, + nineteenFour, + sheet); + addCell(dc); + } + else + { + nv.setNumberFormat(formattingRecords.getNumberFormat(ixf)); + addCell(nv); + } + } + } + else if (type == Type.NUMBER) + { + NumberRecord nr = new NumberRecord(r, formattingRecords, sheet); + + if (formattingRecords.isDate(nr.getXFIndex())) + { + DateCell dc = new DateRecord(nr, + nr.getXFIndex(), + formattingRecords, + nineteenFour, sheet); + addCell(dc); + } + else + { + addCell(nr); + } + } + else if (type == Type.BOOLERR) + { + BooleanRecord br = new BooleanRecord(r, formattingRecords, sheet); + + if (br.isError()) + { + ErrorRecord er = new ErrorRecord(br.getRecord(), formattingRecords, + sheet); + addCell(er); + } + else + { + addCell(br); + } + } + else if (type == Type.PRINTGRIDLINES) + { + printGridLinesRecord = new PrintGridLinesRecord(r); + settings.setPrintGridLines(printGridLinesRecord.getPrintGridLines()); + } + else if (type == Type.PRINTHEADERS) + { + printHeadersRecord = new PrintHeadersRecord(r); + settings.setPrintHeaders(printHeadersRecord.getPrintHeaders()); + } + else if (type == Type.WINDOW2) + { + window2Record = null; + + if (workbookBof.isBiff8()) + { + window2Record = new Window2Record(r); + } + else + { + window2Record = new Window2Record(r, Window2Record.biff7); + } + + settings.setShowGridLines(window2Record.getShowGridLines()); + settings.setDisplayZeroValues(window2Record.getDisplayZeroValues()); + settings.setSelected(true); + settings.setPageBreakPreviewMode(window2Record.isPageBreakPreview()); + } + else if (type == Type.PANE) + { + PaneRecord pr = new PaneRecord(r); + + if (window2Record != null && + window2Record.getFrozen()) + { + settings.setVerticalFreeze(pr.getRowsVisible()); + settings.setHorizontalFreeze(pr.getColumnsVisible()); + } + } + else if (type == Type.CONTINUE) + { + // don't know what this is for, but keep hold of it anyway + continueRecord = new ContinueRecord(r); + } + else if (type == Type.NOTE) + { + if (!workbookSettings.getDrawingsDisabled()) + { + NoteRecord nr = new NoteRecord(r); + + // Get the comment for the object id + Comment comment = (Comment) comments.remove + (new Integer(nr.getObjectId())); + + if (comment == null) + { + logger.warn(" cannot find comment for note id " + + nr.getObjectId() + "...ignoring"); + } + else + { + comment.setNote(nr); + + drawings.add(comment); + + addCellComment(comment.getColumn(), + comment.getRow(), + comment.getText(), + comment.getWidth(), + comment.getHeight()); + } + } + } + else if (type == Type.ARRAY) + { + ; + } + else if (type == Type.PROTECT) + { + ProtectRecord pr = new ProtectRecord(r); + settings.setProtected(pr.isProtected()); + } + else if (type == Type.SHAREDFORMULA) + { + if (sharedFormula == null) + { + logger.warn("Shared template formula is null - " + + "trying most recent formula template"); + SharedFormulaRecord lastSharedFormula = + (SharedFormulaRecord) sharedFormulas.get(sharedFormulas.size() - 1); + + if (lastSharedFormula != null) + { + sharedFormula = lastSharedFormula.getTemplateFormula(); + } + } + + SharedFormulaRecord sfr = new SharedFormulaRecord + (r, sharedFormula, workbook, workbook, sheet); + sharedFormulas.add(sfr); + sharedFormula = null; + } + else if (type == Type.FORMULA || type == Type.FORMULA2) + { + FormulaRecord fr = new FormulaRecord(r, + excelFile, + formattingRecords, + workbook, + workbook, + sheet, + workbookSettings); + + if (fr.isShared()) + { + BaseSharedFormulaRecord prevSharedFormula = sharedFormula; + sharedFormula = (BaseSharedFormulaRecord) fr.getFormula(); + + // See if it fits in any of the shared formulas + sharedFormulaAdded = addToSharedFormulas(sharedFormula); + + if (sharedFormulaAdded) + { + sharedFormula = prevSharedFormula; + } + + // If we still haven't added the previous base shared formula, + // revert it to an ordinary formula and add it to the cell + if (!sharedFormulaAdded && prevSharedFormula != null) + { + // Do nothing. It's possible for the biff file to contain the + // record sequence + // FORMULA-SHRFMLA-FORMULA-SHRFMLA-FORMULA-FORMULA-FORMULA + // ie. it first lists all the formula templates, then it + // lists all the individual formulas + addCell(revertSharedFormula(prevSharedFormula)); + } + } + else + { + Cell cell = fr.getFormula(); + try + { + // See if the formula evaluates to date + if (fr.getFormula().getType() == CellType.NUMBER_FORMULA) + { + NumberFormulaRecord nfr = (NumberFormulaRecord) fr.getFormula(); + if (formattingRecords.isDate(nfr.getXFIndex())) + { + cell = new DateFormulaRecord(nfr, + formattingRecords, + workbook, + workbook, + nineteenFour, + sheet); + } + } + + addCell(cell); + } + catch (FormulaException e) + { + // Something has gone wrong trying to read the formula data eg. it + // might be unsupported biff7 data + logger.warn + (CellReferenceHelper.getCellReference + (cell.getColumn(), cell.getRow()) + " " + e.getMessage()); + } + } + } + else if (type == Type.LABEL) + { + LabelRecord lr = null; + + if (workbookBof.isBiff8()) + { + lr = new LabelRecord(r, formattingRecords, sheet, workbookSettings); + } + else + { + lr = new LabelRecord(r, formattingRecords, sheet, workbookSettings, + LabelRecord.biff7); + } + addCell(lr); + } + else if (type == Type.RSTRING) + { + RStringRecord lr = null; + + // RString records are obsolete in biff 8 + Assert.verify(!workbookBof.isBiff8()); + lr = new RStringRecord(r, formattingRecords, + sheet, workbookSettings, + RStringRecord.biff7); + addCell(lr); + } + else if (type == Type.NAME) + { + ; + } + else if (type == Type.PASSWORD) + { + PasswordRecord pr = new PasswordRecord(r); + settings.setPasswordHash(pr.getPasswordHash()); + } + else if (type == Type.ROW) + { + RowRecord rr = new RowRecord(r); + + // See if the row has anything funny about it + if (!rr.isDefaultHeight() || + !rr.matchesDefaultFontHeight() || + rr.isCollapsed() || + rr.hasDefaultFormat() || + rr.getOutlineLevel() != 0) + { + rowProperties.add(rr); + } + } + else if (type == Type.BLANK) + { + if (!workbookSettings.getIgnoreBlanks()) + { + BlankCell bc = new BlankCell(r, formattingRecords, sheet); + addCell(bc); + } + } + else if (type == Type.MULBLANK) + { + if (!workbookSettings.getIgnoreBlanks()) + { + MulBlankRecord mulblank = new MulBlankRecord(r); + + // Get the individual cell records from the multiple record + int num = mulblank.getNumberOfColumns(); + + for (int i = 0; i < num; i++) + { + int ixf = mulblank.getXFIndex(i); + + MulBlankCell mbc = new MulBlankCell + (mulblank.getRow(), + mulblank.getFirstColumn() + i, + ixf, + formattingRecords, + sheet); + + addCell(mbc); + } + } + } + else if (type == Type.SCL) + { + SCLRecord scl = new SCLRecord(r); + settings.setZoomFactor(scl.getZoomFactor()); + } + else if (type == Type.COLINFO) + { + ColumnInfoRecord cir = new ColumnInfoRecord(r); + columnInfosArray.add(cir); + } + else if (type == Type.HEADER) + { + HeaderRecord hr = null; + if (workbookBof.isBiff8()) + { + hr = new HeaderRecord(r, workbookSettings); + } + else + { + hr = new HeaderRecord(r, workbookSettings, HeaderRecord.biff7); + } + + HeaderFooter header = new HeaderFooter(hr.getHeader()); + settings.setHeader(header); + } + else if (type == Type.FOOTER) + { + FooterRecord fr = null; + if (workbookBof.isBiff8()) + { + fr = new FooterRecord(r, workbookSettings); + } + else + { + fr = new FooterRecord(r, workbookSettings, FooterRecord.biff7); + } + + HeaderFooter footer = new HeaderFooter(fr.getFooter()); + settings.setFooter(footer); + } + else if (type == Type.SETUP) + { + SetupRecord sr = new SetupRecord(r); + + // If the setup record has its not initialized bit set, then + // use the sheet settings default values + if (sr.getInitialized()) + { + if (sr.isPortrait()) + { + settings.setOrientation(PageOrientation.PORTRAIT); + } + else + { + settings.setOrientation(PageOrientation.LANDSCAPE); + } + if (sr.isRightDown()) + { + settings.setPageOrder(PageOrder.RIGHT_THEN_DOWN); + } + else + { + settings.setPageOrder(PageOrder.DOWN_THEN_RIGHT); + } + settings.setPaperSize(PaperSize.getPaperSize(sr.getPaperSize())); + settings.setHeaderMargin(sr.getHeaderMargin()); + settings.setFooterMargin(sr.getFooterMargin()); + settings.setScaleFactor(sr.getScaleFactor()); + settings.setPageStart(sr.getPageStart()); + settings.setFitWidth(sr.getFitWidth()); + settings.setFitHeight(sr.getFitHeight()); + settings.setHorizontalPrintResolution + (sr.getHorizontalPrintResolution()); + settings.setVerticalPrintResolution(sr.getVerticalPrintResolution()); + settings.setCopies(sr.getCopies()); + + if (workspaceOptions != null) + { + settings.setFitToPages(workspaceOptions.getFitToPages()); + } + } + } + else if (type == Type.WSBOOL) + { + workspaceOptions = new WorkspaceInformationRecord(r); + } + else if (type == Type.DEFCOLWIDTH) + { + DefaultColumnWidthRecord dcwr = new DefaultColumnWidthRecord(r); + settings.setDefaultColumnWidth(dcwr.getWidth()); + } + else if (type == Type.DEFAULTROWHEIGHT) + { + DefaultRowHeightRecord drhr = new DefaultRowHeightRecord(r); + if (drhr.getHeight() != 0) + { + settings.setDefaultRowHeight(drhr.getHeight()); + } + } + else if (type == Type.CONDFMT) + { + ConditionalFormatRangeRecord cfrr = + new ConditionalFormatRangeRecord(r); + condFormat = new ConditionalFormat(cfrr); + conditionalFormats.add(condFormat); + } + else if (type == Type.CF) + { + ConditionalFormatRecord cfr = new ConditionalFormatRecord(r); + condFormat.addCondition(cfr); + } + else if (type == Type.FILTERMODE) + { + filterMode = new FilterModeRecord(r); + } + else if (type == Type.AUTOFILTERINFO) + { + autoFilterInfo = new AutoFilterInfoRecord(r); + } + else if (type == Type.AUTOFILTER) + { + if (!workbookSettings.getAutoFilterDisabled()) + { + AutoFilterRecord af = new AutoFilterRecord(r); + + if (autoFilter == null) + { + autoFilter = new AutoFilter(filterMode, autoFilterInfo); + filterMode = null; + autoFilterInfo = null; + } + + autoFilter.add(af); + } + } + else if (type == Type.LEFTMARGIN) + { + MarginRecord m = new LeftMarginRecord(r); + settings.setLeftMargin(m.getMargin()); + } + else if (type == Type.RIGHTMARGIN) + { + MarginRecord m = new RightMarginRecord(r); + settings.setRightMargin(m.getMargin()); + } + else if (type == Type.TOPMARGIN) + { + MarginRecord m = new TopMarginRecord(r); + settings.setTopMargin(m.getMargin()); + } + else if (type == Type.BOTTOMMARGIN) + { + MarginRecord m = new BottomMarginRecord(r); + settings.setBottomMargin(m.getMargin()); + } + else if (type == Type.HORIZONTALPAGEBREAKS) + { + HorizontalPageBreaksRecord dr = null; + + if (workbookBof.isBiff8()) + { + dr = new HorizontalPageBreaksRecord(r); + } + else + { + dr = new HorizontalPageBreaksRecord + (r, HorizontalPageBreaksRecord.biff7); + } + rowBreaks = dr.getRowBreaks(); + } + else if (type == Type.VERTICALPAGEBREAKS) + { + VerticalPageBreaksRecord dr = null; + + if (workbookBof.isBiff8()) + { + dr = new VerticalPageBreaksRecord(r); + } + else + { + dr = new VerticalPageBreaksRecord + (r, VerticalPageBreaksRecord.biff7); + } + columnBreaks = dr.getColumnBreaks(); + } + else if (type == Type.PLS) + { + plsRecord = new PLSRecord(r); + } + else if (type == Type.DVAL) + { + if (!workbookSettings.getCellValidationDisabled()) + { + DataValidityListRecord dvlr = new DataValidityListRecord(r); + if (dvlr.getObjectId() == -1) + { + if (msoRecord != null && objRecord == null) + { + // there is a drop down associated with this data validation + if (drawingData == null) + { + drawingData = new DrawingData(); + } + + Drawing2 d2 = new Drawing2(msoRecord, drawingData, + workbook.getDrawingGroup()); + drawings.add(d2); + msoRecord = null; + + dataValidation = new DataValidation(dvlr); + } + else + { + // no drop down + dataValidation = new DataValidation(dvlr); + } + } + else if (objectIds.contains(new Integer(dvlr.getObjectId()))) + { + dataValidation = new DataValidation(dvlr); + } + else + { + logger.warn("object id " + dvlr.getObjectId() + " referenced " + + " by data validity list record not found - ignoring"); + } + } + } + else if (type == Type.HCENTER) + { + CentreRecord hr = new CentreRecord(r); + settings.setHorizontalCentre(hr.isCentre()); + } + else if (type == Type.VCENTER) + { + CentreRecord vc = new CentreRecord(r); + settings.setVerticalCentre(vc.isCentre()); + } + else if (type == Type.DV) + { + if (!workbookSettings.getCellValidationDisabled()) + { + DataValiditySettingsRecord dvsr = + new DataValiditySettingsRecord(r, + workbook, + workbook, + workbook.getSettings()); + if (dataValidation != null) + { + dataValidation.add(dvsr); + addCellValidation(dvsr.getFirstColumn(), + dvsr.getFirstRow(), + dvsr.getLastColumn(), + dvsr.getLastRow(), + dvsr); + } + else + { + logger.warn("cannot add data validity settings"); + } + } + } + else if (type == Type.OBJ) + { + objRecord = new ObjRecord(r); + + if (!workbookSettings.getDrawingsDisabled()) + { + // sometimes excel writes out continue records instead of drawing + // records, so forcibly hack the stashed continue record into + // a drawing record + if (msoRecord == null && continueRecord != null) + { + logger.warn("Cannot find drawing record - using continue record"); + msoRecord = new MsoDrawingRecord(continueRecord.getRecord()); + continueRecord = null; + } + handleObjectRecord(objRecord, msoRecord, comments); + objectIds.add(new Integer(objRecord.getObjectId())); + } + + // Save chart handling until the chart BOF record appears + if (objRecord.getType() != ObjRecord.CHART) + { + objRecord = null; + msoRecord = null; + } + } + else if (type == Type.MSODRAWING) + { + if (!workbookSettings.getDrawingsDisabled()) + { + if (msoRecord != null) + { + // For form controls, a rogue MSODRAWING record can crop up + // after the main one. Add these into the drawing data + drawingData.addRawData(msoRecord.getData()); + } + msoRecord = new MsoDrawingRecord(r); + + if (firstMsoRecord) + { + msoRecord.setFirst(); + firstMsoRecord = false; + } + } + } + else if (type == Type.BUTTONPROPERTYSET) + { + buttonPropertySet = new ButtonPropertySetRecord(r); + } + else if (type == Type.CALCMODE) + { + CalcModeRecord cmr = new CalcModeRecord(r); + settings.setAutomaticFormulaCalculation(cmr.isAutomatic()); + } + else if (type == Type.SAVERECALC) + { + SaveRecalcRecord cmr = new SaveRecalcRecord(r); + settings.setRecalculateFormulasBeforeSave(cmr.getRecalculateOnSave()); + } + else if (type == Type.GUTS) + { + GuttersRecord gr = new GuttersRecord(r); + maxRowOutlineLevel = + gr.getRowOutlineLevel() > 0 ? gr.getRowOutlineLevel() - 1 : 0; + maxColumnOutlineLevel = + gr.getColumnOutlineLevel() > 0 ? gr.getRowOutlineLevel() - 1 : 0; + } + else if (type == Type.BOF) + { + BOFRecord br = new BOFRecord(r); + Assert.verify(!br.isWorksheet()); + + int startpos = excelFile.getPos() - r.getLength() - 4; + + // Skip to the end of the nested bof + // Thanks to Rohit for spotting this + Record r2 = excelFile.next(); + while (r2.getCode() != Type.EOF.value) + { + r2 = excelFile.next(); + } + + if (br.isChart()) + { + if (!workbook.getWorkbookBof().isBiff8()) + { + logger.warn("only biff8 charts are supported"); + } + else + { + if (drawingData == null) + { + drawingData = new DrawingData(); + } + + if (!workbookSettings.getDrawingsDisabled()) + { + Chart chart = new Chart(msoRecord, objRecord, drawingData, + startpos, excelFile.getPos(), + excelFile, workbookSettings); + charts.add(chart); + + if (workbook.getDrawingGroup() != null) + { + workbook.getDrawingGroup().add(chart); + } + } + } + + // Reset the drawing records + msoRecord = null; + objRecord = null; + } + + // If this worksheet is just a chart, then the EOF reached + // represents the end of the sheet as well as the end of the chart + if (sheetBof.isChart()) + { + cont = false; + } + } + else if (type == Type.EOF) + { + cont = false; + } + } + + // Restore the file to its accurate position + excelFile.restorePos(); + + // Add any out of bounds cells + if (outOfBoundsCells.size() > 0) + { + handleOutOfBoundsCells(); + } + + // Add all the shared formulas to the sheet as individual formulas + Iterator i = sharedFormulas.iterator(); + + while (i.hasNext()) + { + SharedFormulaRecord sfr = (SharedFormulaRecord) i.next(); + + Cell[] sfnr = sfr.getFormulas(formattingRecords, nineteenFour); + + for (int sf = 0; sf < sfnr.length; sf++) + { + addCell(sfnr[sf]); + } + } + + // If the last base shared formula wasn't added to the sheet, then + // revert it to an ordinary formula and add it + if (!sharedFormulaAdded && sharedFormula != null) + { + addCell(revertSharedFormula(sharedFormula)); + } + + // If there is a stray msoDrawing record, then flag to the drawing group + // that one has been omitted + if (msoRecord != null && workbook.getDrawingGroup() != null) + { + workbook.getDrawingGroup().setDrawingsOmitted(msoRecord, objRecord); + } + + // Check that the comments hash is empty + if (!comments.isEmpty()) + { + logger.warn("Not all comments have a corresponding Note record"); + } + } + + /** + * Sees if the shared formula belongs to any of the shared formula + * groups + * + * @param fr the candidate shared formula + * @return TRUE if the formula was added, FALSE otherwise + */ + private boolean addToSharedFormulas(BaseSharedFormulaRecord fr) + { + boolean added = false; + SharedFormulaRecord sfr = null; + + for (int i=0, size=sharedFormulas.size(); i row && cells[row].length > col) + { + c = cells[row][col]; + } + + if (c == null) + { + logger.warn("Cell at " + + CellReferenceHelper.getCellReference(col, row) + + " not present - adding a blank"); + MulBlankCell mbc = new MulBlankCell(row, + col, + 0, + formattingRecords, + sheet); + CellFeatures cf = new CellFeatures(); + cf.setValidationSettings(dvsr); + mbc.setCellFeatures(cf); + addCell(mbc); + + return; + } + + if (c instanceof CellFeaturesAccessor) + { + CellFeaturesAccessor cv = (CellFeaturesAccessor) c; + CellFeatures cf = cv.getCellFeatures(); + + if (cf == null) + { + cf = new CellFeatures(); + cv.setCellFeatures(cf); + } + + cf.setValidationSettings(dvsr); + } + else + { + logger.warn("Not able to add comment to cell type " + + c.getClass().getName() + + " at " + CellReferenceHelper.getCellReference(col, row)); + } + } + } + } + + /** + * Reads in the object record + * + * @param objRecord the obj record + * @param msoRecord the mso drawing record read in earlier + * @param comments the hash map of comments + */ + private void handleObjectRecord(ObjRecord objRecord, + MsoDrawingRecord msoRecord, + HashMap comments) + { + if (msoRecord == null) + { + logger.warn("Object record is not associated with a drawing " + + " record - ignoring"); + return; + } + + try + { + // Handle images + if (objRecord.getType() == ObjRecord.PICTURE) + { + if (drawingData == null) + { + drawingData = new DrawingData(); + } + + Drawing drawing = new Drawing(msoRecord, + objRecord, + drawingData, + workbook.getDrawingGroup(), + sheet); + drawings.add(drawing); + return; + } + + // Handle comments + if (objRecord.getType() == ObjRecord.EXCELNOTE) + { + if (drawingData == null) + { + drawingData = new DrawingData(); + } + + Comment comment = new Comment(msoRecord, + objRecord, + drawingData, + workbook.getDrawingGroup(), + workbookSettings); + + // Sometimes Excel writes out Continue records instead of drawing + // records, so forcibly hack all of these into a drawing record + Record r2 = excelFile.next(); + if (r2.getType() == Type.MSODRAWING || r2.getType() == Type.CONTINUE) + { + MsoDrawingRecord mso = new MsoDrawingRecord(r2); + comment.addMso(mso); + r2 = excelFile.next(); + } + Assert.verify(r2.getType() == Type.TXO); + TextObjectRecord txo = new TextObjectRecord(r2); + comment.setTextObject(txo); + + r2 = excelFile.next(); + Assert.verify(r2.getType() == Type.CONTINUE); + ContinueRecord text = new ContinueRecord(r2); + comment.setText(text); + + r2 = excelFile.next(); + if (r2.getType() == Type.CONTINUE) + { + ContinueRecord formatting = new ContinueRecord(r2); + comment.setFormatting(formatting); + } + + comments.put(new Integer(comment.getObjectId()), comment); + return; + } + + // Handle combo boxes + if (objRecord.getType() == ObjRecord.COMBOBOX) + { + if (drawingData == null) + { + drawingData = new DrawingData(); + } + + ComboBox comboBox = new ComboBox(msoRecord, + objRecord, + drawingData, + workbook.getDrawingGroup(), + workbookSettings); + drawings.add(comboBox); + return; + } + + // Handle form buttons + if (objRecord.getType() == ObjRecord.BUTTON) + { + if (drawingData == null) + { + drawingData = new DrawingData(); + } + + Button button = new Button(msoRecord, + objRecord, + drawingData, + workbook.getDrawingGroup(), + workbookSettings); + + Record r2 = excelFile.next(); + if (r2.getType() == Type.MSODRAWING) + { + MsoDrawingRecord mso = new MsoDrawingRecord(r2); + button.addMso(mso); + r2 = excelFile.next(); + } + Assert.verify(r2.getType() == Type.TXO); + TextObjectRecord txo = new TextObjectRecord(r2); + button.setTextObject(txo); + + r2 = excelFile.next(); + Assert.verify(r2.getType() == Type.CONTINUE); + ContinueRecord text = new ContinueRecord(r2); + button.setText(text); + + r2 = excelFile.next(); + if (r2.getType() == Type.CONTINUE) + { + ContinueRecord formatting = new ContinueRecord(r2); + button.setFormatting(formatting); + } + + drawings.add(button); + + return; + } + + // Handle other types + if (objRecord.getType() != ObjRecord.CHART) + { + logger.warn(objRecord.getType() + " on sheet \"" + + sheet.getName() + + "\" not supported - omitting"); + + if (drawingData == null) + { + drawingData = new DrawingData(); + } + + drawingData.addData(msoRecord.getData()); + + if (workbook.getDrawingGroup() != null) // can be null for Excel 95 + { + workbook.getDrawingGroup().setDrawingsOmitted(msoRecord, + objRecord); + } + + return; + } + } + catch (DrawingDataException e) + { + logger.warn(e.getMessage() + + "...disabling drawings for the remainder of the workbook"); + workbookSettings.setDrawingsDisabled(true); + } + } + + /** + * Gets the drawing data - for use as part of the Escher debugging tool + */ + DrawingData getDrawingData() + { + return drawingData; + } + + /** + * Handle any cells which fall outside of the bounds specified within + * the dimension record + */ + private void handleOutOfBoundsCells() + { + int resizedRows = numRows; + int resizedCols = numCols; + + // First, determine the new bounds + for (Iterator i = outOfBoundsCells.iterator() ; i.hasNext() ;) + { + Cell cell = (Cell) i.next(); + resizedRows = Math.max(resizedRows, cell.getRow() + 1); + resizedCols = Math.max(resizedCols, cell.getColumn() + 1); + } + + logger.warn("Some cells exceeded the specified bounds. Resizing " + + "sheet dimensions from " + + numCols + "x"+numRows + " to " + + resizedCols + "x" + resizedRows); + + // Resize the columns, if necessary + if (resizedCols > numCols) + { + for (int r = 0 ; r < numRows ; r++) + { + Cell[] newRow = new Cell[resizedCols]; + Cell[] oldRow = cells[r]; + System.arraycopy(oldRow, 0, newRow, 0, oldRow.length); + cells[r] = newRow; + } + } + + // Resize the rows, if necessary + if (resizedRows > numRows) + { + Cell[][] newCells = new Cell[resizedRows][]; + System.arraycopy(cells, 0, newCells, 0, cells.length); + cells = newCells; + + // Create the new rows + for (int i = numRows; i < resizedRows; i++) + { + newCells[i] = new Cell[resizedCols]; + } + } + + numRows = resizedRows; + numCols = resizedCols; + + // Now add all the out of bounds cells into the new cells + for (Iterator i = outOfBoundsCells.iterator(); i.hasNext(); ) + { + Cell cell = (Cell) i.next(); + addCell(cell); + } + + outOfBoundsCells.clear(); + } + + /** + * Accessor for the maximum column outline level + * + * @return the maximum column outline level, or 0 if no outlines/groups + */ + public int getMaxColumnOutlineLevel() + { + return maxColumnOutlineLevel; + } + + /** + * Accessor for the maximum row outline level + * + * @return the maximum row outline level, or 0 if no outlines/groups + */ + public int getMaxRowOutlineLevel() + { + return maxRowOutlineLevel; + } +} Index: 3rdParty_sources/jexcelapi/jxl/read/biff/SortRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/SortRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/SortRecord.java 17 Aug 2012 14:50:57 -0000 1.1 @@ -0,0 +1,179 @@ +/********************************************************************* +* +* Copyright (C) 2004 Andrew Khan, Al Mantei +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.RecordData; +import jxl.biff.StringHelper; +import jxl.biff.Type; + +/** + * A storage area for the last Sort dialog box area + */ +public class SortRecord extends RecordData +{ + private int col1Size; + private int col2Size; + private int col3Size; + private String col1Name; + private String col2Name; + private String col3Name; + private byte optionFlags; + private boolean sortColumns = false; + private boolean sortKey1Desc = false; + private boolean sortKey2Desc = false; + private boolean sortKey3Desc = false; + private boolean sortCaseSensitive = false; + /** + * Constructs this object from the raw data + * + * @param r the raw data + */ + public SortRecord(Record r) + { + super(Type.SORT); + + byte[] data = r.getData(); + + optionFlags = data[0]; + + sortColumns = ((optionFlags & 0x01) != 0); + sortKey1Desc = ((optionFlags & 0x02) != 0); + sortKey2Desc = ((optionFlags & 0x04) != 0); + sortKey3Desc = ((optionFlags & 0x08) != 0); + sortCaseSensitive = ((optionFlags & 0x10) != 0); + + // data[1] contains sort list index - not implemented... + + col1Size = data[2]; + col2Size = data[3]; + col3Size = data[4]; + int curPos = 5; + if (data[curPos++] == 0x00) + { + col1Name = new String(data, curPos, col1Size); + curPos += col1Size; + } + else + { + col1Name = StringHelper.getUnicodeString(data, col1Size, curPos); + curPos += col1Size * 2; + } + + if (col2Size > 0) + { + if (data[curPos++] == 0x00) + { + col2Name = new String(data, curPos, col2Size); + curPos += col2Size; + } + else + { + col2Name = StringHelper.getUnicodeString(data, col2Size, curPos); + curPos += col2Size * 2; + } + } + else + { + col2Name = ""; + } + if (col3Size > 0) + { + if (data[curPos++] == 0x00) + { + col3Name = new String(data, curPos, col3Size); + curPos += col3Size; + } + else + { + col3Name = StringHelper.getUnicodeString(data, col3Size, curPos); + curPos += col3Size * 2; + } + } + else + { + col3Name = ""; + } + } + + /** + * Accessor for the 1st Sort Column Name + * + * @return the 1st Sort Column Name + */ + public String getSortCol1Name() { + return col1Name; + } + /** + * Accessor for the 2nd Sort Column Name + * + * @return the 2nd Sort Column Name + */ + public String getSortCol2Name() { + return col2Name; + } + /** + * Accessor for the 3rd Sort Column Name + * + * @return the 3rd Sort Column Name + */ + public String getSortCol3Name() { + return col3Name; + } + /** + * Accessor for the Sort by Columns flag + * + * @return the Sort by Columns flag + */ + public boolean getSortColumns() { + return sortColumns; + } + /** + * Accessor for the Sort Column 1 Descending flag + * + * @return the Sort Column 1 Descending flag + */ + public boolean getSortKey1Desc() { + return sortKey1Desc; + } + /** + * Accessor for the Sort Column 2 Descending flag + * + * @return the Sort Column 2 Descending flag + */ + public boolean getSortKey2Desc() { + return sortKey2Desc; + } + /** + * Accessor for the Sort Column 3 Descending flag + * + * @return the Sort Column 3 Descending flag + */ + public boolean getSortKey3Desc() { + return sortKey3Desc; + } + /** + * Accessor for the Sort Case Sensitivity flag + * + * @return the Sort Case Secsitivity flag + */ + public boolean getSortCaseSensitive() { + return sortCaseSensitive; + } +} Index: 3rdParty_sources/jexcelapi/jxl/read/biff/StringFormulaRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/StringFormulaRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/StringFormulaRecord.java 17 Aug 2012 14:50:57 -0000 1.1 @@ -0,0 +1,290 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import common.Assert; +import common.Logger; + + +import jxl.CellType; +import jxl.LabelCell; +import jxl.StringFormulaCell; +import jxl.WorkbookSettings; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.IntegerHelper; +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; + +/** + * A string formula's last calculated value + */ +class StringFormulaRecord extends CellValue + implements LabelCell, FormulaData, StringFormulaCell +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(StringFormulaRecord.class); + + /** + * The last calculated value of the formula + */ + private String value; + + /** + * A handle to the class needed to access external sheets + */ + private ExternalSheet externalSheet; + + /** + * A handle to the name table + */ + private WorkbookMethods nameTable; + + /** + * The formula as an excel string + */ + private String formulaString; + + /** + * The raw data + */ + private byte[] data; + + /** + * Constructs this object from the raw data. We need to use the excelFile + * to retrieve the String record which follows this formula record + * + * @param t the raw data + * @param excelFile the excel file + * @param fr the formatting records + * @param es the external sheet records + * @param nt the workbook + * @param si the sheet impl + * @param ws the workbook settings + */ + public StringFormulaRecord(Record t, File excelFile, + FormattingRecords fr, + ExternalSheet es, + WorkbookMethods nt, + SheetImpl si, + WorkbookSettings ws) + { + super(t, fr, si); + + externalSheet = es; + nameTable = nt; + + data = getRecord().getData(); + + int pos = excelFile.getPos(); + + // Look for the string record in one of the records after the + // formula. Put a cap on it to prevent looping + + Record nextRecord = excelFile.next(); + int count = 0; + while (nextRecord.getType() != Type.STRING && count < 4) + { + nextRecord = excelFile.next(); + count++; + } + Assert.verify(count < 4, " @ " + pos); + byte[] stringData = nextRecord.getData(); + + // Read in any continuation records + nextRecord = excelFile.peek(); + while (nextRecord.getType() == Type.CONTINUE) + { + nextRecord = excelFile.next(); // move the pointer within the data + byte[] d = new byte[stringData.length + nextRecord.getLength() - 1]; + System.arraycopy(stringData, 0, d, 0, stringData.length); + System.arraycopy(nextRecord.getData(), 1, d, + stringData.length, nextRecord.getLength() - 1); + stringData = d; + nextRecord = excelFile.peek(); + } + readString(stringData, ws); + } + + /** + * Constructs this object from the raw data. Used when reading in formula + * strings which evaluate to null (in the case of some IF statements) + * + * @param t the raw data + * @param fr the formatting records + * @param es the external sheet records + * @param nt the workbook + * @param si the sheet impl + * @param ws the workbook settings + */ + public StringFormulaRecord(Record t, + FormattingRecords fr, + ExternalSheet es, + WorkbookMethods nt, + SheetImpl si) + { + super(t, fr, si); + + externalSheet = es; + nameTable = nt; + + data = getRecord().getData(); + value = ""; + } + + + /** + * Reads in the string + * + * @param d the data + * @param ws the workbook settings + */ + private void readString(byte[] d, WorkbookSettings ws) + { + int pos = 0; + int chars = IntegerHelper.getInt(d[0], d[1]); + + if (chars == 0) + { + value=""; + return; + } + pos += 2; + int optionFlags = d[pos]; + pos++; + + if ((optionFlags & 0xf) != optionFlags) + { + // Uh oh - looks like a plain old string, not unicode + // Recalculate all the positions + pos = 0; + chars = IntegerHelper.getInt(d[0], (byte) 0); + optionFlags = d[1]; + pos = 2; + } + + // See if it is an extended string + boolean extendedString = ((optionFlags & 0x04) != 0); + + // See if string contains formatting information + boolean richString = ((optionFlags & 0x08) != 0); + + if (richString) + { + pos += 2; + } + + if (extendedString) + { + pos += 4; + } + + // See if string is ASCII (compressed) or unicode + boolean asciiEncoding = ((optionFlags & 0x01) == 0); + + if (asciiEncoding) + { + value = StringHelper.getString(d, chars, pos, ws); + } + else + { + value = StringHelper.getUnicodeString(d, chars, pos); + } + } + + /** + * Interface method which returns the value + * + * @return the last calculated value of the formula + */ + public String getContents() + { + return value; + } + + /** + * Interface method which returns the value + * + * @return the last calculated value of the formula + */ + public String getString() + { + return value; + } + + /** + * Returns the cell type + * + * @return The cell type + */ + public CellType getType() + { + return CellType.STRING_FORMULA; + } + + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array + * + * @return the raw record data + */ + public byte[] getFormulaData() throws FormulaException + { + if (!getSheet().getWorkbook().getWorkbookBof().isBiff8()) + { + throw new FormulaException(FormulaException.BIFF8_SUPPORTED); + } + + // Lop off the standard information + byte[] d = new byte[data.length - 6]; + System.arraycopy(data, 6, d, 0, data.length - 6); + + return d; + } + + /** + * Gets the formula as an excel string + * + * @return the formula as an excel string + * @exception FormulaException + */ + public String getFormula() throws FormulaException + { + if (formulaString == null) + { + byte[] tokens = new byte[data.length - 22]; + System.arraycopy(data, 22, tokens, 0, tokens.length); + FormulaParser fp = new FormulaParser + (tokens, this, externalSheet, nameTable, + getSheet().getWorkbook().getSettings()); + fp.parse(); + formulaString = fp.getFormula(); + } + + return formulaString; + } + +} Index: 3rdParty_sources/jexcelapi/jxl/read/biff/SupbookRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/SupbookRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/SupbookRecord.java 17 Aug 2012 14:50:56 -0000 1.1 @@ -0,0 +1,338 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import common.Logger; + +import jxl.WorkbookSettings; +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.StringHelper; + +/** + * A record containing the references to the various sheets (internal and + * external) referenced by formulas in this workbook + */ +public class SupbookRecord extends RecordData +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(SupbookRecord.class); + + /** + * The type of this supbook record + */ + private Type type; + + /** + * The number of sheets - internal & external supbooks only + */ + private int numSheets; + + /** + * The name of the external file + */ + private String fileName; + + /** + * The names of the external sheets + */ + private String[] sheetNames; + + /** + * The type of supbook this refers to + */ + private static class Type {}; + + + public static final Type INTERNAL = new Type(); + public static final Type EXTERNAL = new Type(); + public static final Type ADDIN = new Type(); + public static final Type LINK = new Type(); + public static final Type UNKNOWN = new Type(); + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param ws the workbook settings + */ + SupbookRecord(Record t, WorkbookSettings ws) + { + super(t); + byte[] data = getRecord().getData(); + + // First deduce the type + if (data.length == 4) + { + if (data[2] == 0x01 && data[3] == 0x04) + { + type = INTERNAL; + } + else if (data[2] == 0x01 && data[3] == 0x3a) + { + type = ADDIN; + } + else + { + type = UNKNOWN; + } + } + else if (data[0] == 0 && data[1] == 0) + { + type = LINK; + } + else + { + type = EXTERNAL; + } + + if (type == INTERNAL) + { + numSheets = IntegerHelper.getInt(data[0], data[1]); + } + + if (type == EXTERNAL) + { + readExternal(data, ws); + } + } + + /** + * Reads the external data records + * + * @param data the data + * @param ws the workbook settings + */ + private void readExternal(byte[] data, WorkbookSettings ws) + { + numSheets = IntegerHelper.getInt(data[0], data[1]); + + // subtract file name encoding from the length + int ln = IntegerHelper.getInt(data[2], data[3]) - 1; + int pos = 0; + + if (data[4] == 0) + { + // non-unicode string + int encoding = data[5]; + pos = 6; + if (encoding == 0) + { + fileName = StringHelper.getString(data, ln, pos, ws); + pos += ln; + } + else + { + fileName = getEncodedFilename(data, ln, pos); + pos += ln; + } + } + else + { + // unicode string + int encoding = IntegerHelper.getInt(data[5], data[6]); + pos = 7; + if (encoding == 0) + { + fileName = StringHelper.getUnicodeString(data, ln, pos); + pos += ln * 2; + } + else + { + fileName = getUnicodeEncodedFilename(data, ln, pos); + pos += ln * 2; + } + } + + sheetNames = new String[numSheets]; + + for (int i = 0; i < sheetNames.length; i++) + { + ln = IntegerHelper.getInt(data[pos], data[pos + 1]); + + if (data[pos + 2] == 0x0) + { + sheetNames[i] = StringHelper.getString(data, ln, pos + 3, ws); + pos += ln + 3; + } + else if (data[pos + 2] == 0x1) + { + sheetNames[i] = StringHelper.getUnicodeString(data, ln, pos + 3); + pos += ln * 2 + 3; + } + } + } + + /** + * Gets the type of this supbook record + * + * @return the type of this supbook + */ + public Type getType() + { + return type; + } + + /** + * Gets the number of sheets. This will only be non-zero for internal + * and external supbooks + * + * @return the number of sheets + */ + public int getNumberOfSheets() + { + return numSheets; + } + + /** + * Gets the name of the external file + * + * @return the name of the external file + */ + public String getFileName() + { + return fileName; + } + + /** + * Gets the name of the external sheet + * + * @param i the index of the external sheet + * @return the name of the sheet + */ + public String getSheetName(int i) + { + return sheetNames[i]; + } + + /** + * Gets the data - used when copying a spreadsheet + * + * @return the raw external sheet data + */ + public byte[] getData() + { + return getRecord().getData(); + } + + /** + * Gets the encoded string from the data array + * + * @param data the data + * @param ln length of the string + * @param pos the position in the array + * @return the string + */ + private String getEncodedFilename(byte[] data, int ln, int pos) + { + StringBuffer buf = new StringBuffer(); + int endpos = pos + ln; + while (pos < endpos) + { + char c = (char) data[pos]; + + if (c == '\u0001') + { + // next character is a volume letter + pos++; + c = (char) data[pos]; + buf.append(c); + buf.append(":\\\\"); + } + else if (c == '\u0002') + { + // file is on the same volume + buf.append('\\'); + } + else if (c == '\u0003') + { + // down directory + buf.append('\\'); + } + else if (c == '\u0004') + { + // up directory + buf.append("..\\"); + } + else + { + // just add on the character + buf.append(c); + } + + pos++; + } + + return buf.toString(); + } + + /** + * Gets the encoded string from the data array + * + * @param data the data + * @param ln length of the string + * @param pos the position in the array + * @return the string + */ + private String getUnicodeEncodedFilename(byte[] data, int ln, int pos) + { + StringBuffer buf = new StringBuffer(); + int endpos = pos + ln * 2; + while (pos < endpos) + { + char c = (char) IntegerHelper.getInt(data[pos], data[pos + 1]); + + if (c == '\u0001') + { + // next character is a volume letter + pos += 2; + c = (char) IntegerHelper.getInt(data[pos], data[pos + 1]); + buf.append(c); + buf.append(":\\\\"); + } + else if (c == '\u0002') + { + // file is on the same volume + buf.append('\\'); + } + else if (c == '\u0003') + { + // down directory + buf.append('\\'); + } + else if (c == '\u0004') + { + // up directory + buf.append("..\\"); + } + else + { + // just add on the character + buf.append(c); + } + + pos += 2; + } + + return buf.toString(); + } +} Index: 3rdParty_sources/jexcelapi/jxl/read/biff/TopMarginRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/TopMarginRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/TopMarginRecord.java 17 Aug 2012 14:50:57 -0000 1.1 @@ -0,0 +1,37 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.Type; + +/** + * Record for the left margin settings + */ +class TopMarginRecord extends MarginRecord +{ + /** + * Constructor + * @param r the record + */ + TopMarginRecord(Record r) + { + super(Type.TOPMARGIN, r); + } +} Index: 3rdParty_sources/jexcelapi/jxl/read/biff/VerticalPageBreaksRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/VerticalPageBreaksRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/VerticalPageBreaksRecord.java 17 Aug 2012 14:50:57 -0000 1.1 @@ -0,0 +1,108 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import common.Logger; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; + +/** + * Contains the cell dimensions of this worksheet + */ +class VerticalPageBreaksRecord extends RecordData +{ + /** + * The logger + */ + private final Logger logger = Logger.getLogger + (VerticalPageBreaksRecord.class); + + /** + * The row page breaks + */ + private int[] columnBreaks; + + /** + * Dummy indicators for overloading the constructor + */ + private static class Biff7 {}; + public static Biff7 biff7 = new Biff7(); + + /** + * Constructs the dimensions from the raw data + * + * @param t the raw data + */ + public VerticalPageBreaksRecord(Record t) + { + super(t); + + byte[] data = t.getData(); + + int numbreaks = IntegerHelper.getInt(data[0], data[1]); + int pos = 2; + columnBreaks = new int[numbreaks]; + + for (int i = 0; i < numbreaks; i++) + { + columnBreaks[i] = IntegerHelper.getInt(data[pos], data[pos + 1]); + pos += 6; + } + } + + /** + * Constructs the dimensions from the raw data + * + * @param t the raw data + * @param biff7 an indicator to initialise this record for biff 7 format + */ + public VerticalPageBreaksRecord(Record t, Biff7 biff7) + { + super(t); + + byte[] data = t.getData(); + int numbreaks = IntegerHelper.getInt(data[0], data[1]); + int pos = 2; + columnBreaks = new int[numbreaks]; + for (int i = 0; i < numbreaks; i++) + { + columnBreaks[i] = IntegerHelper.getInt(data[pos], data[pos + 1]); + pos += 2; + } + } + + /** + * Gets the row breaks + * + * @return the row breaks on the current sheet + */ + public int[] getColumnBreaks() + { + return columnBreaks; + } +} + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/Window2Record.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/Window2Record.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/Window2Record.java 17 Aug 2012 14:50:56 -0000 1.1 @@ -0,0 +1,208 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import common.Logger; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; + +/** + * Contains the cell dimensions of this worksheet + */ +class Window2Record extends RecordData +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(Window2Record.class); + + /** + * Selected flag + */ + private boolean selected; + /** + * Show grid lines flag + */ + private boolean showGridLines; + /** + * Display zero values flag + */ + private boolean displayZeroValues; + /** + * The window contains frozen panes + */ + private boolean frozenPanes; + /** + * The window contains panes that are frozen but not split + */ + private boolean frozenNotSplit; + /** + * The view mode: normal or pagebreakpreview + */ + private boolean pageBreakPreviewMode; + + /** + * The page break preview magnification + */ + private int pageBreakPreviewMagnification; + + /** + * The normal view magnification + */ + private int normalMagnification; + + // Dummy overload + private static class Biff7 {}; + public static final Biff7 biff7 = new Biff7(); + + /** + * Constructs the dimensions from the raw data + * + * @param t the raw data + */ + public Window2Record(Record t) + { + super(t); + byte[] data = t.getData(); + + int options = IntegerHelper.getInt(data[0], data[1]); + + selected = ((options & 0x200) != 0); + showGridLines = ((options & 0x02) != 0); + frozenPanes = ((options & 0x08) != 0); + displayZeroValues = ((options & 0x10) != 0); + frozenNotSplit = ((options & 0x100) != 0); + pageBreakPreviewMode = ((options & 0x800) != 0); + + pageBreakPreviewMagnification = IntegerHelper.getInt(data[10], data[11]); + normalMagnification = IntegerHelper.getInt(data[12], data[13]); + } + + /** + * Constructs the dimensions from the raw data. Dummy overload for + * biff7 workbooks + * + * @param t the raw data + * @param dummy the overload + */ + public Window2Record(Record t, Biff7 biff7) + { + super(t); + byte[] data = t.getData(); + + int options = IntegerHelper.getInt(data[0], data[1]); + + selected = ((options & 0x200) != 0); + showGridLines = ((options & 0x02) != 0); + frozenPanes = ((options & 0x08) != 0); + displayZeroValues = ((options & 0x10) != 0); + frozenNotSplit = ((options & 0x100) != 0); + pageBreakPreviewMode = ((options & 0x800) != 0); + } + + /** + * Accessor for the selected flag + * + * @return TRUE if this sheet is selected, FALSE otherwise + */ + public boolean isSelected() + { + return selected; + } + + /** + * Accessor for the show grid lines flag + * + * @return TRUE to show grid lines, FALSE otherwise + */ + public boolean getShowGridLines() + { + return showGridLines; + } + + /** + * Accessor for the zero values flag + * + * @return TRUE if this sheet displays zero values, FALSE otherwise + */ + public boolean getDisplayZeroValues() + { + return displayZeroValues; + } + + /** + * Accessor for the frozen panes flag + * + * @return TRUE if this contains frozen panes, FALSE otherwise + */ + public boolean getFrozen() + { + return frozenPanes; + } + + /** + * Accessor for the frozen not split flag + * + * @return TRUE if this contains frozen, FALSE otherwise + */ + public boolean getFrozenNotSplit() + { + return frozenNotSplit; + } + + /** + * Accessor for the page break preview mode + * + * @return TRUE if this sheet is in page break preview, FALSE otherwise + */ + public boolean isPageBreakPreview() + { + return pageBreakPreviewMode; + } + + /** + * Accessor for the page break preview magnification + * + * @return the cached paged break preview magnfication factor in percent + */ + public int getPageBreakPreviewMagnificaiton() + { + return pageBreakPreviewMagnification; + } + + /** + * Accessor for the normal view magnification + * + * @return the cached normal view magnfication factor in percent + */ + public int getNormalMagnificaiton() + { + return normalMagnification; + } + +} + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/read/biff/WorkbookParser.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/read/biff/WorkbookParser.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/read/biff/WorkbookParser.java 17 Aug 2012 14:50:57 -0000 1.1 @@ -0,0 +1,1235 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.read.biff; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; + +import common.Assert; +import common.Logger; + +import jxl.Cell; +import jxl.Range; +import jxl.Sheet; +import jxl.Workbook; +import jxl.WorkbookSettings; +import jxl.biff.BuiltInName; +import jxl.biff.CellReferenceHelper; +import jxl.biff.EmptyCell; +import jxl.biff.FontRecord; +import jxl.biff.Fonts; +import jxl.biff.FormatRecord; +import jxl.biff.FormattingRecords; +import jxl.biff.NumFormatRecordsException; +import jxl.biff.PaletteRecord; +import jxl.biff.RangeImpl; +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.biff.WorkbookMethods; +import jxl.biff.XFRecord; +import jxl.biff.drawing.DrawingGroup; +import jxl.biff.drawing.MsoDrawingGroupRecord; +import jxl.biff.drawing.Origin; +import jxl.biff.formula.ExternalSheet; + +/** + * Parses the biff file passed in, and builds up an internal representation of + * the spreadsheet + */ +public class WorkbookParser extends Workbook + implements ExternalSheet, WorkbookMethods +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(WorkbookParser.class); + + /** + * The excel file + */ + private File excelFile; + /** + * The number of open bofs + */ + private int bofs; + /** + * Indicates whether or not the dates are based around the 1904 date system + */ + private boolean nineteenFour; + /** + * The shared string table + */ + private SSTRecord sharedStrings; + /** + * The names of all the worksheets + */ + private ArrayList boundsheets; + /** + * The xf records + */ + private FormattingRecords formattingRecords; + /** + * The fonts used by this workbook + */ + private Fonts fonts; + + /** + * The sheets contained in this workbook + */ + private ArrayList sheets; + + /** + * The last sheet accessed + */ + private SheetImpl lastSheet; + + /** + * The index of the last sheet retrieved + */ + private int lastSheetIndex; + + /** + * The named records found in this workbook + */ + private HashMap namedRecords; + + /** + * The list of named records + */ + private ArrayList nameTable; + + /** + * The list of add in functions + */ + private ArrayList addInFunctions; + + /** + * The external sheet record. Used by formulas, and names + */ + private ExternalSheetRecord externSheet; + + /** + * The list of supporting workbooks - used by formulas + */ + private ArrayList supbooks; + + /** + * The bof record for this workbook + */ + private BOFRecord workbookBof; + + /** + * The Mso Drawing Group record for this workbook + */ + private MsoDrawingGroupRecord msoDrawingGroup; + + /** + * The property set record associated with this workbook + */ + private ButtonPropertySetRecord buttonPropertySet; + + /** + * Workbook protected flag + */ + private boolean wbProtected; + + /** + * Contains macros flag + */ + private boolean containsMacros; + + /** + * The workbook settings + */ + private WorkbookSettings settings; + + /** + * The drawings contained in this workbook + */ + private DrawingGroup drawingGroup; + + /** + * The country record (containing the language and regional settings) + * for this workbook + */ + private CountryRecord countryRecord; + + /** + * Constructs this object from the raw excel data + * + * @param f the excel 97 biff file + * @param s the workbook settings + */ + public WorkbookParser(File f, WorkbookSettings s) + { + super(); + excelFile = f; + boundsheets = new ArrayList(10); + fonts = new Fonts(); + formattingRecords = new FormattingRecords(fonts); + sheets = new ArrayList(10); + supbooks = new ArrayList(10); + namedRecords = new HashMap(); + lastSheetIndex = -1; + wbProtected = false; + containsMacros = false; + settings = s; + } + + /** + * Gets the sheets within this workbook. + * NOTE: Use of this method for + * very large worksheets can cause performance and out of memory problems. + * Use the alternative method getSheet() to retrieve each sheet individually + * + * @return an array of the individual sheets + */ + public Sheet[] getSheets() + { + Sheet[] sheetArray = new Sheet[getNumberOfSheets()]; + return (Sheet[]) sheets.toArray(sheetArray); + } + + /** + * Interface method from WorkbookMethods - gets the specified + * sheet within this workbook + * + * @param index the zero based index of the required sheet + * @return The sheet specified by the index + */ + public Sheet getReadSheet(int index) + { + return getSheet(index); + } + + /** + * Gets the specified sheet within this workbook + * + * @param index the zero based index of the required sheet + * @return The sheet specified by the index + */ + public Sheet getSheet(int index) + { + // First see if the last sheet index is the same as this sheet index. + // If so, then the same sheet is being re-requested, so simply + // return it instead of rereading it + if ((lastSheet != null) && lastSheetIndex == index) + { + return lastSheet; + } + + // Flush out all of the cached data in the last sheet + if (lastSheet != null) + { + lastSheet.clear(); + + if (!settings.getGCDisabled()) + { + System.gc(); + } + } + + lastSheet = (SheetImpl) sheets.get(index); + lastSheetIndex = index; + lastSheet.readSheet(); + + return lastSheet; + } + + /** + * Gets the sheet with the specified name from within this workbook + * + * @param name the sheet name + * @return The sheet with the specified name, or null if it is not found + */ + public Sheet getSheet(String name) + { + // Iterate through the boundsheet records + int pos = 0; + boolean found = false; + Iterator i = boundsheets.iterator(); + BoundsheetRecord br = null; + + while (i.hasNext() && !found) + { + br = (BoundsheetRecord) i.next(); + + if (br.getName().equals(name)) + { + found = true; + } + else + { + pos++; + } + } + + return found ? getSheet(pos) : null; + } + + /** + * Gets the sheet names + * + * @return an array of strings containing the sheet names + */ + public String[] getSheetNames() + { + String[] names = new String[boundsheets.size()]; + + BoundsheetRecord br = null; + for (int i = 0; i < names.length; i++) + { + br = (BoundsheetRecord) boundsheets.get(i); + names[i] = br.getName(); + } + + return names; + } + + + /** + * Package protected function which gets the real internal sheet index + * based upon the external sheet reference. This is used for extern sheet + * references which are specified in formulas + * + * @param index the external sheet reference + * @return the actual sheet index + */ + public int getExternalSheetIndex(int index) + { + // For biff7, the whole external reference thing works differently + // Hopefully for our purposes sheet references will all be local + if (workbookBof.isBiff7()) + { + return index; + } + + Assert.verify(externSheet != null); + + int firstTab = externSheet.getFirstTabIndex(index); + + return firstTab; + } + + /** + * Package protected function which gets the real internal sheet index + * based upon the external sheet reference. This is used for extern sheet + * references which are specified in formulas + * + * @param index the external sheet reference + * @return the actual sheet index + */ + public int getLastExternalSheetIndex(int index) + { + // For biff7, the whole external reference thing works differently + // Hopefully for our purposes sheet references will all be local + if (workbookBof.isBiff7()) + { + return index; + } + + Assert.verify(externSheet != null); + + int lastTab = externSheet.getLastTabIndex(index); + + return lastTab; + } + + /** + * Gets the name of the external sheet specified by the index + * + * @param index the external sheet index + * @return the name of the external sheet + */ + public String getExternalSheetName(int index) + { + // For biff7, the whole external reference thing works differently + // Hopefully for our purposes sheet references will all be local + if (workbookBof.isBiff7()) + { + BoundsheetRecord br = (BoundsheetRecord) boundsheets.get(index); + + return br.getName(); + } + + int supbookIndex = externSheet.getSupbookIndex(index); + SupbookRecord sr = (SupbookRecord) supbooks.get(supbookIndex); + + int firstTab = externSheet.getFirstTabIndex(index); + int lastTab = externSheet.getLastTabIndex(index); + String firstTabName = ""; + String lastTabName = ""; + + if (sr.getType() == SupbookRecord.INTERNAL) + { + // It's an internal reference - get the name from the boundsheets list + if (firstTab == 65535) + { + firstTabName = "#REF"; + } + else + { + BoundsheetRecord br = (BoundsheetRecord) boundsheets.get(firstTab); + firstTabName = br.getName(); + } + + if (lastTab==65535) + { + lastTabName = "#REF"; + } + else + { + BoundsheetRecord br = (BoundsheetRecord) boundsheets.get(lastTab); + lastTabName = br.getName(); + } + + String sheetName = (firstTab == lastTab) ? firstTabName : + firstTabName + ':' + lastTabName; + + // if the sheet name contains apostrophes then escape them + sheetName = sheetName.indexOf('\'') == -1 ? sheetName : + StringHelper.replace(sheetName, "\'", "\'\'"); + + + // if the sheet name contains spaces, then enclose in quotes + return sheetName.indexOf(' ') == -1 ? sheetName : + '\'' + sheetName + '\''; + } + else if (sr.getType() == SupbookRecord.EXTERNAL) + { + // External reference - get the sheet name from the supbook record + StringBuffer sb = new StringBuffer(); + java.io.File fl = new java.io.File(sr.getFileName()); + sb.append("'"); + sb.append(fl.getAbsolutePath()); + sb.append("["); + sb.append(fl.getName()); + sb.append("]"); + sb.append((firstTab == 65535) ? "#REF" : sr.getSheetName(firstTab)); + if (lastTab != firstTab) + { + sb.append(sr.getSheetName(lastTab)); + } + sb.append("'"); + return sb.toString(); + } + + // An unknown supbook - return unkown + return "[UNKNOWN]"; + } + + /** + * Gets the name of the external sheet specified by the index + * + * @param index the external sheet index + * @return the name of the external sheet + */ + public String getLastExternalSheetName(int index) + { + // For biff7, the whole external reference thing works differently + // Hopefully for our purposes sheet references will all be local + if (workbookBof.isBiff7()) + { + BoundsheetRecord br = (BoundsheetRecord) boundsheets.get(index); + + return br.getName(); + } + + int supbookIndex = externSheet.getSupbookIndex(index); + SupbookRecord sr = (SupbookRecord) supbooks.get(supbookIndex); + + int lastTab = externSheet.getLastTabIndex(index); + + if (sr.getType() == SupbookRecord.INTERNAL) + { + // It's an internal reference - get the name from the boundsheets list + if (lastTab == 65535) + { + return "#REF"; + } + else + { + BoundsheetRecord br = (BoundsheetRecord) boundsheets.get(lastTab); + return br.getName(); + } + } + else if (sr.getType() == SupbookRecord.EXTERNAL) + { + // External reference - get the sheet name from the supbook record + StringBuffer sb = new StringBuffer(); + java.io.File fl = new java.io.File(sr.getFileName()); + sb.append("'"); + sb.append(fl.getAbsolutePath()); + sb.append("["); + sb.append(fl.getName()); + sb.append("]"); + sb.append((lastTab == 65535) ? "#REF" : sr.getSheetName(lastTab)); + sb.append("'"); + return sb.toString(); + } + + // An unknown supbook - return unkown + return "[UNKNOWN]"; + } + + /** + * Returns the number of sheets in this workbook + * + * @return the number of sheets in this workbook + */ + public int getNumberOfSheets() + { + return sheets.size(); + } + + /** + * Closes this workbook, and frees makes any memory allocated available + * for garbage collection + */ + public void close() + { + if (lastSheet != null) + { + lastSheet.clear(); + } + excelFile.clear(); + + if (!settings.getGCDisabled()) + { + System.gc(); + } + } + + /** + * Adds the sheet to the end of the array + * + * @param s the sheet to add + */ + final void addSheet(Sheet s) + { + sheets.add(s); + } + + /** + * Does the hard work of building up the object graph from the excel bytes + * + * @exception BiffException + * @exception PasswordException if the workbook is password protected + */ + protected void parse() throws BiffException, PasswordException + { + Record r = null; + + BOFRecord bof = new BOFRecord(excelFile.next()); + workbookBof = bof; + bofs++; + + if (!bof.isBiff8() && !bof.isBiff7()) + { + throw new BiffException(BiffException.unrecognizedBiffVersion); + } + + if (!bof.isWorkbookGlobals()) + { + throw new BiffException(BiffException.expectedGlobals); + } + ArrayList continueRecords = new ArrayList(); + ArrayList localNames = new ArrayList(); + nameTable = new ArrayList(); + addInFunctions = new ArrayList(); + + // Skip to the firs worksheet + while (bofs == 1) + { + r = excelFile.next(); + + if (r.getType() == Type.SST) + { + continueRecords.clear(); + Record nextrec = excelFile.peek(); + while (nextrec.getType() == Type.CONTINUE) + { + continueRecords.add(excelFile.next()); + nextrec = excelFile.peek(); + } + + // cast the array + Record[] records = new Record[continueRecords.size()]; + records = (Record[]) continueRecords.toArray(records); + + sharedStrings = new SSTRecord(r, records, settings); + } + else if (r.getType() == Type.FILEPASS) + { + throw new PasswordException(); + } + else if (r.getType() == Type.NAME) + { + NameRecord nr = null; + + if (bof.isBiff8()) + { + nr = new NameRecord(r, settings, namedRecords.size()); + + } + else + { + nr = new NameRecord(r, settings, namedRecords.size(), + NameRecord.biff7); + } + + if (nr.isGlobal()) + { + namedRecords.put(nr.getName(), nr); + nameTable.add(nr); + } + else + { + localNames.add(nr); + } + } + else if (r.getType() == Type.FONT) + { + FontRecord fr = null; + + if (bof.isBiff8()) + { + fr = new FontRecord(r, settings); + } + else + { + fr = new FontRecord(r, settings, FontRecord.biff7); + } + fonts.addFont(fr); + } + else if (r.getType() == Type.PALETTE) + { + PaletteRecord palette = new PaletteRecord(r); + formattingRecords.setPalette(palette); + } + else if (r.getType() == Type.NINETEENFOUR) + { + NineteenFourRecord nr = new NineteenFourRecord(r); + nineteenFour = nr.is1904(); + } + else if (r.getType() == Type.FORMAT) + { + FormatRecord fr = null; + if (bof.isBiff8()) + { + fr = new FormatRecord(r, settings, FormatRecord.biff8); + } + else + { + fr = new FormatRecord(r, settings, FormatRecord.biff7); + } + try + { + formattingRecords.addFormat(fr); + } + catch (NumFormatRecordsException e) + { + e.printStackTrace(); + // This should not happen. Bomb out + Assert.verify(false, e.getMessage()); + } + } + else if (r.getType() == Type.XF) + { + XFRecord xfr = null; + if (bof.isBiff8()) + { + xfr = new XFRecord(r, settings, XFRecord.biff8); + } + else + { + xfr = new XFRecord(r, settings, XFRecord.biff7); + } + + try + { + formattingRecords.addStyle(xfr); + } + catch (NumFormatRecordsException e) + { + // This should not happen. Bomb out + Assert.verify(false, e.getMessage()); + } + } + else if (r.getType() == Type.BOUNDSHEET) + { + BoundsheetRecord br = null; + + if (bof.isBiff8()) + { + br = new BoundsheetRecord(r, settings); + } + else + { + br = new BoundsheetRecord(r, BoundsheetRecord.biff7); + } + + if (br.isSheet()) + { + boundsheets.add(br); + } + else if (br.isChart() && !settings.getDrawingsDisabled()) + { + boundsheets.add(br); + } + } + else if (r.getType() == Type.EXTERNSHEET) + { + if (bof.isBiff8()) + { + externSheet = new ExternalSheetRecord(r, settings); + } + else + { + externSheet = new ExternalSheetRecord(r, settings, + ExternalSheetRecord.biff7); + } + } + else if (r.getType() == Type.CODEPAGE) + { + CodepageRecord cr = new CodepageRecord(r); + settings.setCharacterSet(cr.getCharacterSet()); + } + else if (r.getType() == Type.SUPBOOK) + { + Record nextrec = excelFile.peek(); + while (nextrec.getType() == Type.CONTINUE) + { + r.addContinueRecord(excelFile.next()); + nextrec = excelFile.peek(); + } + + SupbookRecord sr = new SupbookRecord(r, settings); + supbooks.add(sr); + } + else if (r.getType() == Type.EXTERNNAME) + { + ExternalNameRecord enr = new ExternalNameRecord(r, settings); + + if (enr.isAddInFunction()) + { + addInFunctions.add(enr.getName()); + } + } + else if (r.getType() == Type.PROTECT) + { + ProtectRecord pr = new ProtectRecord(r); + wbProtected = pr.isProtected(); + } + else if (r.getType() == Type.OBJPROJ) + { + containsMacros = true; + } + else if (r.getType() == Type.COUNTRY) + { + countryRecord = new CountryRecord(r); + } + else if (r.getType() == Type.MSODRAWINGGROUP) + { + if (!settings.getDrawingsDisabled()) + { + msoDrawingGroup = new MsoDrawingGroupRecord(r); + + if (drawingGroup == null) + { + drawingGroup = new DrawingGroup(Origin.READ); + } + + drawingGroup.add(msoDrawingGroup); + + Record nextrec = excelFile.peek(); + while (nextrec.getType() == Type.CONTINUE) + { + drawingGroup.add(excelFile.next()); + nextrec = excelFile.peek(); + } + } + } + else if (r.getType() == Type.BUTTONPROPERTYSET) + { + buttonPropertySet = new ButtonPropertySetRecord(r); + } + else if (r.getType() == Type.EOF) + { + bofs--; + } + } + + bof = null; + if (excelFile.hasNext()) + { + r = excelFile.next(); + + if (r.getType() == Type.BOF) + { + bof = new BOFRecord(r); + } + } + + // Only get sheets for which there is a corresponding Boundsheet record + while (bof != null && getNumberOfSheets() < boundsheets.size()) + { + if (!bof.isBiff8() && !bof.isBiff7()) + { + throw new BiffException(BiffException.unrecognizedBiffVersion); + } + + if (bof.isWorksheet()) + { + // Read the sheet in + SheetImpl s = new SheetImpl(excelFile, + sharedStrings, + formattingRecords, + bof, + workbookBof, + nineteenFour, + this); + + BoundsheetRecord br = (BoundsheetRecord) boundsheets.get + (getNumberOfSheets()); + s.setName(br.getName()); + s.setHidden(br.isHidden()); + addSheet(s); + } + else if (bof.isChart()) + { + // Read the sheet in + SheetImpl s = new SheetImpl(excelFile, + sharedStrings, + formattingRecords, + bof, + workbookBof, + nineteenFour, + this); + + BoundsheetRecord br = (BoundsheetRecord) boundsheets.get + (getNumberOfSheets()); + s.setName(br.getName()); + s.setHidden(br.isHidden()); + addSheet(s); + } + else + { + logger.warn("BOF is unrecognized"); + + + while (excelFile.hasNext() && r.getType() != Type.EOF) + { + r = excelFile.next(); + } + } + + // The next record will normally be a BOF or empty padding until + // the end of the block is reached. In exceptionally unlucky cases, + // the last EOF will coincide with a block division, so we have to + // check there is more data to retrieve. + // Thanks to liamg for spotting this + bof = null; + if (excelFile.hasNext()) + { + r = excelFile.next(); + + if (r.getType() == Type.BOF) + { + bof = new BOFRecord(r); + } + } + } + + // Add all the local names to the specific sheets + for (Iterator it = localNames.iterator() ; it.hasNext() ;) + { + NameRecord nr = (NameRecord) it.next(); + + if (nr.getBuiltInName() == null) + { + logger.warn("Usage of a local non-builtin name"); + } + else if (nr.getBuiltInName() == BuiltInName.PRINT_AREA || + nr.getBuiltInName() == BuiltInName.PRINT_TITLES) + { + // appears to use the internal tab number rather than the + // external sheet index + SheetImpl s = (SheetImpl) sheets.get(nr.getSheetRef() - 1); + s.addLocalName(nr); + } + } + } + + /** + * Accessor for the formattingRecords, used by the WritableWorkbook + * when creating a copy of this + * + * @return the formatting records + */ + public FormattingRecords getFormattingRecords() + { + return formattingRecords; + } + + /** + * Accessor for the externSheet, used by the WritableWorkbook + * when creating a copy of this + * + * @return the external sheet record + */ + public ExternalSheetRecord getExternalSheetRecord() + { + return externSheet; + } + + /** + * Accessor for the MsoDrawingGroup, used by the WritableWorkbook + * when creating a copy of this + * + * @return the Mso Drawing Group record + */ + public MsoDrawingGroupRecord getMsoDrawingGroupRecord() + { + return msoDrawingGroup; + } + + /** + * Accessor for the supbook records, used by the WritableWorkbook + * when creating a copy of this + * + * @return the supbook records + */ + public SupbookRecord[] getSupbookRecords() + { + SupbookRecord[] sr = new SupbookRecord[supbooks.size()]; + return (SupbookRecord[]) supbooks.toArray(sr); + } + + /** + * Accessor for the name records. Used by the WritableWorkbook when + * creating a copy of this + * + * @return the array of names + */ + public NameRecord[] getNameRecords() + { + NameRecord[] na = new NameRecord[nameTable.size()]; + return (NameRecord[]) nameTable.toArray(na); + } + + /** + * Accessor for the fonts, used by the WritableWorkbook + * when creating a copy of this + * @return the fonts used in this workbook + */ + public Fonts getFonts() + { + return fonts; + } + + /** + * Returns the cell for the specified location eg. "Sheet1!A4". + * This is identical to using the CellReferenceHelper with its + * associated performance overheads, consequently it should + * be use sparingly + * + * @param loc the cell to retrieve + * @return the cell at the specified location + */ + public Cell getCell(String loc) + { + Sheet s = getSheet(CellReferenceHelper.getSheet(loc)); + return s.getCell(loc); + } + + /** + * Gets the named cell from this workbook. If the name refers to a + * range of cells, then the cell on the top left is returned. If + * the name cannot be found, null is returned + * + * @param name the name of the cell/range to search for + * @return the cell in the top left of the range if found, NULL + * otherwise + */ + public Cell findCellByName(String name) + { + NameRecord nr = (NameRecord) namedRecords.get(name); + + if (nr == null) + { + return null; + } + + NameRecord.NameRange[] ranges = nr.getRanges(); + + // Go and retrieve the first cell in the first range + Sheet s = getSheet(getExternalSheetIndex(ranges[0].getExternalSheet())); + int col = ranges[0].getFirstColumn(); + int row = ranges[0].getFirstRow(); + + // If the sheet boundaries fall short of the named cell, then return + // an empty cell to stop an exception being thrown + if (col > s.getColumns() || row > s.getRows()) + { + return new EmptyCell(col, row); + } + + Cell cell = s.getCell(col, row); + + return cell; + } + + /** + * Gets the named range from this workbook. The Range object returns + * contains all the cells from the top left to the bottom right + * of the range. + * If the named range comprises an adjacent range, + * the Range[] will contain one object; for non-adjacent + * ranges, it is necessary to return an array of length greater than + * one. + * If the named range contains a single cell, the top left and + * bottom right cell will be the same cell + * + * @param name the name to find + * @return the range of cells + */ + public Range[] findByName(String name) + { + NameRecord nr = (NameRecord) namedRecords.get(name); + + if (nr == null) + { + return null; + } + + NameRecord.NameRange[] ranges = nr.getRanges(); + + Range[] cellRanges = new Range[ranges.length]; + + for (int i = 0; i < ranges.length; i++) + { + cellRanges[i] = new RangeImpl + (this, + getExternalSheetIndex(ranges[i].getExternalSheet()), + ranges[i].getFirstColumn(), + ranges[i].getFirstRow(), + getLastExternalSheetIndex(ranges[i].getExternalSheet()), + ranges[i].getLastColumn(), + ranges[i].getLastRow()); + } + + return cellRanges; + } + + /** + * Gets the named ranges + * + * @return the list of named cells within the workbook + */ + public String[] getRangeNames() + { + Object[] keys = namedRecords.keySet().toArray(); + String[] names = new String[keys.length]; + System.arraycopy(keys, 0, names, 0, keys.length); + + return names; + } + + /** + * Method used when parsing formulas to make sure we are trying + * to parse a supported biff version + * + * @return the BOF record + */ + public BOFRecord getWorkbookBof() + { + return workbookBof; + } + + /** + * Determines whether the sheet is protected + * + * @return whether or not the sheet is protected + */ + public boolean isProtected() + { + return wbProtected; + } + + /** + * Accessor for the settings + * + * @return the workbook settings + */ + public WorkbookSettings getSettings() + { + return settings; + } + + /** + * Accessor/implementation method for the external sheet reference + * + * @param sheetName the sheet name to look for + * @return the external sheet index + */ + public int getExternalSheetIndex(String sheetName) + { + return 0; + } + + /** + * Accessor/implementation method for the external sheet reference + * + * @param sheetName the sheet name to look for + * @return the external sheet index + */ + public int getLastExternalSheetIndex(String sheetName) + { + return 0; + } + + /** + * Gets the name at the specified index + * + * @param index the index into the name table + * @return the name of the cell + */ + public String getName(int index) + { + Assert.verify(index >= 0 && index < nameTable.size()); + return ((NameRecord) nameTable.get(index)).getName(); + } + + /** + * Gets the index of the name record for the name + * + * @param name the name to search for + * @return the index in the name table + */ + public int getNameIndex(String name) + { + NameRecord nr = (NameRecord) namedRecords.get(name); + + return nr != null ? nr.getIndex() : 0; + } + + /** + * Accessor for the drawing group + * + * @return the drawing group + */ + public DrawingGroup getDrawingGroup() + { + return drawingGroup; + } + + /** + * Accessor for the CompoundFile. For this feature to return non-null + * value, the propertySets feature in WorkbookSettings must be enabled + * and the workbook must contain additional property sets. This + * method is used during the workbook copy + * + * @return the base compound file if it contains additional data items + * and property sets are enabled + */ + public CompoundFile getCompoundFile() + { + return excelFile.getCompoundFile(); + } + + /** + * Accessor for the containsMacros + * + * @return TRUE if this workbook contains macros, FALSE otherwise + */ + public boolean containsMacros() + { + return containsMacros; + } + + /** + * Accessor for the button property set, used during copying + * + * @return the button property set + */ + public ButtonPropertySetRecord getButtonPropertySet() + { + return buttonPropertySet; + } + + /** + * Accessor for the country record, using during copying + * + * @return the country record read in + */ + public CountryRecord getCountryRecord() + { + return countryRecord; + } + + /** + * Accessor for addin function names + * + * @return list of add in function names + */ + public String[] getAddInFunctionNames() + { + String[] addins = new String[0]; + return (String[]) addInFunctions.toArray(addins); + } + + /** + * Gets the sheet index in this workbook. Used when importing a sheet + * + * @param sheet the sheet + * @return the 0-based sheet index, or -1 if it is not found + */ + public int getIndex(Sheet sheet) + { + String name = sheet.getName(); + int index = -1; + int pos = 0; + + for (Iterator i = boundsheets.iterator() ; i.hasNext() && index == -1 ;) + { + BoundsheetRecord br = (BoundsheetRecord) i.next(); + + if (br.getName().equals(name)) + { + index = pos; + } + else + { + pos++; + } + } + + return index; + } +} + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/write/Alignment.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/Alignment.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/Alignment.java 17 Aug 2012 14:51:22 -0000 1.1 @@ -0,0 +1,39 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write; + +/** + * Enumeration class which contains the various alignments for data within a + * cell + * + * @deprecated Repackaged as jxl.Alignment. This version is retained + * for backwards compatibility + */ +public final class Alignment extends jxl.format.Alignment +{ + /** + * Private constructor + */ + private Alignment() + { + super(0, null); + } +} + Index: 3rdParty_sources/jexcelapi/jxl/write/Blank.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/Blank.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/Blank.java 17 Aug 2012 14:51:21 -0000 1.1 @@ -0,0 +1,95 @@ +/********************************************************************* +* +* Copyright (C) 2001 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write; + +import jxl.Cell; +import jxl.format.CellFormat; +import jxl.write.biff.BlankRecord; + +/** + * A blank cell. Despite not having any contents, it may contain + * formatting information. Such cells are typically used when creating + * templates + */ +public class Blank extends BlankRecord implements WritableCell +{ + /** + * Creates a cell which, when added to the sheet, will be presented at the + * specified column and row co-ordinates + * + * @param c the column + * @param r the row + */ + public Blank(int c, int r) + { + super(c, r); + } + + /** + * Creates a cell which, when added to the sheet, will be presented at the + * specified column and row co-ordinates + * in the manner specified by the CellFormat parameter + * + * @param c the column + * @param r the row + * @param st the cell format + */ + public Blank(int c, int r, CellFormat st) + { + super(c, r, st); + } + + /** + * Constructor used internally by the application when making a writable + * copy of a spreadsheet being read in + * + * @param lc the cell to copy + */ + public Blank(Cell lc) + { + super(lc); + } + + + /** + * Copy constructor used for deep copying + * + * @param col the column + * @param row the row + * @param b the balnk cell to copy + */ + protected Blank(int col, int row, Blank b) + { + super(col, row, b); + } + + /** + * Implementation of the deep copy function + * + * @param col the column which the new cell will occupy + * @param row the row which the new cell will occupy + * @return a copy of this cell, which can then be added to the sheet + */ + public WritableCell copyTo(int col, int row) + { + return new Blank(col, row, this); + } +} + Index: 3rdParty_sources/jexcelapi/jxl/write/BoldStyle.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/BoldStyle.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/BoldStyle.java 17 Aug 2012 14:51:22 -0000 1.1 @@ -0,0 +1,37 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write; + +/** + * Enumeration class containing the various bold styles for data + */ +public final class BoldStyle extends jxl.format.BoldStyle +{ + /** + * Constructor + * + * @param val a dummy value + */ + private BoldStyle(int val) + { + super(0, ""); + } +} + Index: 3rdParty_sources/jexcelapi/jxl/write/Boolean.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/Boolean.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/Boolean.java 17 Aug 2012 14:51:22 -0000 1.1 @@ -0,0 +1,103 @@ +/********************************************************************* +* +* Copyright (C) 2001 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write; + +import jxl.BooleanCell; +import jxl.format.CellFormat; +import jxl.write.biff.BooleanRecord; + +/** + * A cell, created by user applications, which contains a boolean (or + * in some cases an error) value + */ +public class Boolean extends BooleanRecord implements WritableCell, BooleanCell +{ + /** + * Constructs a boolean value, which, when added to a spreadsheet, will + * display the specified value at the column/row position indicated. + * + * @param c the column + * @param r the row + * @param val the value + */ + public Boolean(int c, int r, boolean val) + { + super(c, r, val); + } + + /** + * Constructs a boolean, which, when added to a spreadsheet, will display the + * specified value at the column/row position with the specified CellFormat. + * The CellFormat may specify font information + * + * @param c the column + * @param r the row + * @param val the value + * @param st the cell format + */ + public Boolean(int c, int r, boolean val, CellFormat st) + { + super(c, r, val, st); + } + + /** + * Constructor used internally by the application when making a writable + * copy of a spreadsheet that has been read in + * + * @param nc the cell to copy + */ + public Boolean(BooleanCell nc) + { + super(nc); + } + + /** + * Copy constructor used for deep copying + * + * @param col the column + * @param row the row + * @param b the cell to copy + */ + protected Boolean(int col, int row, Boolean b) + { + super(col, row, b); + } + /** + * Sets the boolean value for this cell + * + * @param val the value + */ + public void setValue(boolean val) + { + super.setValue(val); + } + + /** + * Implementation of the deep copy function + * + * @param col the column which the new cell will occupy + * @param row the row which the new cell will occupy + * @return a copy of this cell, which can then be added to the sheet + */ + public WritableCell copyTo(int col, int row) + { + return new Boolean(col, row, this); + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/Border.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/Border.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/Border.java 17 Aug 2012 14:51:22 -0000 1.1 @@ -0,0 +1,35 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write; + +/** + * @deprecated repackaged as jxl.format.Border + */ +public final class Border extends jxl.format.Border +{ + /** + * Constructor + */ + private Border() + { + super(null); + } +} + Index: 3rdParty_sources/jexcelapi/jxl/write/BorderLineStyle.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/BorderLineStyle.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/BorderLineStyle.java 17 Aug 2012 14:51:22 -0000 1.1 @@ -0,0 +1,34 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write; + +/** + * @deprecated Repackaged as jxl.format.BorderLineStyle + */ +public final class BorderLineStyle extends jxl.format.BorderLineStyle +{ + /** + * Constructor + */ + private BorderLineStyle() + { + super(0, null); + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/Colour.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/Colour.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/Colour.java 17 Aug 2012 14:51:21 -0000 1.1 @@ -0,0 +1,49 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write; + +/** + * Enumeration class which contains the various colours available within + * the standard Excel colour palette + * + * @deprecated This has been repackaged as jxl.format.Colour + */ +public final class Colour extends jxl.format.Colour +{ + /** + * Constructor + * This is currently just a placeholder for backwards compatibility + */ + private Colour() + { + super(0, null, 0, 0, 0); + } +} + + + + + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/write/DateFormat.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/DateFormat.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/DateFormat.java 17 Aug 2012 14:51:22 -0000 1.1 @@ -0,0 +1,52 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write; + +import java.text.SimpleDateFormat; + +import jxl.biff.DisplayFormat; +import jxl.write.biff.DateFormatRecord; + +/** + * A custom user defined number format which may be instantiated within user + * applications in order to present date and time values in the appropriate + * format. + * The string format used to create a DateFormat adheres to the standard + * java specification, and JExcelApi makes the necessary modifications so + * that it is rendered as its nearest equivalent in Excel. + * Once created, this may be used within a CellFormat object, which in turn + * is a parameter passed to the constructor of the DateTime cell + */ +public class DateFormat extends DateFormatRecord implements DisplayFormat +{ + /** + * Constructor. The date format that is passed should comply to the standard + * Java date formatting conventions + * + * @param format the date format + */ + public DateFormat(String format) + { + super(format); + + // Verify that the format is valid + SimpleDateFormat df = new SimpleDateFormat(format); + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/DateFormats.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/DateFormats.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/DateFormats.java 17 Aug 2012 14:51:22 -0000 1.1 @@ -0,0 +1,220 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write; + +import jxl.biff.DisplayFormat; + +/** + * Static class which contains Excels predefined Date formats + */ +public final class DateFormats +{ + /** + * Inner class which holds the format index + */ + private static class BuiltInFormat implements DisplayFormat + { + /** + * The index of this date format + */ + private int index; + /** + * The excel format + */ + private String formatString; + + /** + * Constructor + * + * @param i the index + * @param s the format string + */ + public BuiltInFormat(int i, String s) + { + index = i; + formatString = s; + } + + /** + * Gets the format index + * + * @return the format index + */ + public int getFormatIndex() + { + return index; + } + + /** + * Interface method which determines whether the index has been set. For + * built ins this is always true + * + * @return TRUE, since this is a built in format + */ + public boolean isInitialized() + { + return true; + } + /** + * Initialize this format with the specified position. Since this is a + * built in format, this is always initialized, so this method body for + * this is empty + * + * @param pos the position in the array + */ + public void initialize(int pos) + { + } + /** + * Determines whether this format is a built in format + * + * @return TRUE, since this is a built in format + */ + public boolean isBuiltIn() + { + return true; + } + /** + * Accesses the excel format string which is applied to the cell + * Note that this is the string that excel uses, and not the java + * equivalent + * + * @return the cell format string + */ + public String getFormatString() + { + return formatString; + } + + /** + * Standard equals method + * + * @param o the object to compare + * @return TRUE if the two objects are equal, FALSE otherwise + */ + public boolean equals(Object o) + { + if (o == this) + { + return true; + } + + if (!(o instanceof BuiltInFormat)) + { + return false; + } + + BuiltInFormat bif = (BuiltInFormat) o; + + return index == bif.index; + } + + /** + * Hash code implementation + * + * @return the hash code + */ + public int hashCode() + { + return index; + } + } + + // The available built in date formats + + /** + * The default format. This is equivalent to a date format of "M/d/yy" + */ + public static final DisplayFormat FORMAT1 = + new BuiltInFormat(0x0e, "M/d/yy"); + /** + * The default format. This is equivalent to a date format of "M/d/yy" + */ + public static final DisplayFormat DEFAULT = FORMAT1; + + /** + * Equivalent to a date format of "d-MMM-yy" + */ + public static final DisplayFormat FORMAT2 = + new BuiltInFormat(0xf, "d-MMM-yy"); + + /** + * Equivalent to a date format of "d-MMM" + */ + public static final DisplayFormat FORMAT3 = + new BuiltInFormat(0x10, "d-MMM"); + + /** + * Equivalent to a date format of "MMM-yy" + */ + public static final DisplayFormat FORMAT4 = + new BuiltInFormat(0x11, "MMM-yy"); + + /** + * Equivalent to a date format of "h:mm a" + */ + public static final DisplayFormat FORMAT5 = + new BuiltInFormat(0x12, "h:mm a"); + + /** + * Equivalent to a date format of "h:mm:ss a" + */ + public static final DisplayFormat FORMAT6 = + new BuiltInFormat(0x13, "h:mm:ss a"); + + /** + * Equivalent to a date format of "H:mm" + */ + public static final DisplayFormat FORMAT7 = + new BuiltInFormat(0x14, "H:mm"); + + /** + * Equivalent to a date format of "H:mm:ss" + */ + public static final DisplayFormat FORMAT8 = + new BuiltInFormat(0x15, "H:mm:ss"); + + /** + * Equivalent to a date format of "M/d/yy H:mm" + */ + public static final DisplayFormat FORMAT9 = + new BuiltInFormat(0x16, "M/d/yy H:mm"); + + /** + * Equivalent to a date format of "mm:ss" + */ + public static final DisplayFormat FORMAT10 = + new BuiltInFormat(0x2d, "mm:ss"); + + /** + * Equivalent to a date format of "H:mm:ss" + */ + public static final DisplayFormat FORMAT11 = + new BuiltInFormat(0x2e, "H:mm:ss"); + + /** + * Equivalent to a date format of "mm:ss.S" + */ + public static final DisplayFormat FORMAT12 = + new BuiltInFormat(0x2f, "H:mm:ss"); +} + + + Index: 3rdParty_sources/jexcelapi/jxl/write/DateTime.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/DateTime.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/DateTime.java 17 Aug 2012 14:51:22 -0000 1.1 @@ -0,0 +1,179 @@ +/********************************************************************* +* +* Copyright (C) 2001 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write; + +import java.util.Date; + +import jxl.DateCell; +import jxl.format.CellFormat; +import jxl.write.biff.DateRecord; + +/** + * A Date which may be created on the fly by a user application and added to a + * spreadsheet + * + * NOTE: By default, all dates will have local timezone information added to + * their UTC value. If this is not desired (eg. if the date entered + * represents an interval eg. 9.83s for the 100m world record, then use + * the overloaded constructor which indicate that the date passed in was + * created under the GMT timezone. It is important that when the date + * was created, an instruction like + * Calendar.setTimeZone(TimeZone.getTimeZone("GMT")) + * was made prior to that + */ +public class DateTime extends DateRecord implements WritableCell, DateCell +{ + /** + * Instance variable for dummy variable overload + */ + public static final GMTDate GMT = new GMTDate(); + + /** + * Constructor. The date will be displayed with date and time components + * using the default date format + * + * @param c the column + * @param r the row + * @param d the date + */ + public DateTime(int c, int r, Date d) + { + super(c, r, d); + } + + /** + * Constructor, which adjusts the specified date to take timezone + * considerations into account. The date passed in will be displayed with + * date and time components using the default date format + * + * @param c the column + * @param r the row + * @param d the date + * @param a dummy overload + */ + public DateTime(int c, int r, Date d, GMTDate a) + { + super(c, r, d, a); + } + + /** + * Constructor which takes the format for this cell + * + * @param c the column + * @param r the row + * @param st the format + * @param d the date + */ + public DateTime(int c, int r, Date d, CellFormat st) + { + super(c, r, d, st); + } + + /** + * Constructor, which adjusts the specified date to take timezone + * considerations into account + * + * @param c the column + * @param r the row + * @param d the date + * @param st the cell format + * @param a the cummy overload + */ + public DateTime(int c, int r, Date d, CellFormat st, GMTDate a) + { + super(c, r, d, st, a); + } + + /** + * Constructor which takes the format for the cell and an indicator + * as to whether this cell is a full date time or purely just a time + * eg. if the spreadsheet is to contain the world record for 100m, then the + * value would be 9.83s, which would be indicated as just a time + * + * @param c the column + * @param r the row + * @param st the style + * @param tim flag indicating that this represents a time + * @param d the date + */ + public DateTime(int c, int r, Date d, CellFormat st, boolean tim) + { + super(c, r, d, st, tim); + } + + /** + * A constructor called by the worksheet when creating a writable version + * of a spreadsheet that has been read in + * + * @param dc the date to copy + */ + public DateTime(DateCell dc) + { + super(dc); + } + + /** + * Copy constructor used for deep copying + * + * @param col the column + * @param row the row + * @param dt the date to copy + */ + protected DateTime(int col, int row, DateTime dt) + { + super(col, row, dt); + } + + + /** + * Sets the date for this cell + * + * @param d the date + */ + public void setDate(Date d) + { + super.setDate(d); + } + + /** + * Sets the date for this cell, performing the necessary timezone adjustments + * + * @param d the date + * @param a the dummy overload + */ + public void setDate(Date d, GMTDate a) + { + super.setDate(d, a); + } + + /** + * Implementation of the deep copy function + * + * @param col the column which the new cell will occupy + * @param row the row which the new cell will occupy + * @return a copy of this cell, which can then be added to the sheet + */ + public WritableCell copyTo(int col, int row) + { + return new DateTime(col, row, this); + } +} + + Index: 3rdParty_sources/jexcelapi/jxl/write/Font.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/Font.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/Font.java 17 Aug 2012 14:51:22 -0000 1.1 @@ -0,0 +1,216 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write; + +import jxl.format.Colour; +import jxl.format.ScriptStyle; +import jxl.format.UnderlineStyle; + +/** + * A class which is instantiated when the user application wishes to specify + * the font for a particular cell + * + * @deprecated Renamed to writable font + */ +public class Font extends WritableFont +{ + /** + * Objects created with this font name will be rendered within Excel as ARIAL + * fonts + * @deprecated + */ + public static final FontName ARIAL = WritableFont.ARIAL; + /** + * Objects created with this font name will be rendered within Excel as TIMES + * fonts + * @deprecated + */ + public static final FontName TIMES = WritableFont.TIMES; + + // The bold styles + + /** + * Indicates that this font should not be presented as bold + * @deprecated + */ + public static final BoldStyle NO_BOLD = WritableFont.NO_BOLD; + /** + * Indicates that this font should be presented in a BOLD style + * @deprecated + */ + public static final BoldStyle BOLD = WritableFont.BOLD; + + // The underline styles + /** + * @deprecated + */ + public static final UnderlineStyle NO_UNDERLINE = + UnderlineStyle.NO_UNDERLINE; + + /** + * @deprecated + */ + public static final UnderlineStyle SINGLE = UnderlineStyle.SINGLE; + + /** + * @deprecated + */ + public static final UnderlineStyle DOUBLE = UnderlineStyle.DOUBLE; + + /** + * @deprecated + */ + public static final UnderlineStyle SINGLE_ACCOUNTING = + UnderlineStyle.SINGLE_ACCOUNTING; + + /** + * @deprecated + */ + public static final UnderlineStyle DOUBLE_ACCOUNTING = + UnderlineStyle.DOUBLE_ACCOUNTING; + + // The script styles + public static final ScriptStyle NORMAL_SCRIPT = ScriptStyle.NORMAL_SCRIPT; + public static final ScriptStyle SUPERSCRIPT = ScriptStyle.SUPERSCRIPT; + public static final ScriptStyle SUBSCRIPT = ScriptStyle.SUBSCRIPT; + + /** + * Creates a default font, vanilla font of the specified face and with + * default point size. + * + * @param fn the font name + * @deprecated Use jxl.write.WritableFont + */ + public Font(FontName fn) + { + super(fn); + } + + /** + * Constructs of font of the specified face and of size given by the + * specified point size + * + * @param ps the point size + * @param fn the font name + * @deprecated use jxl.write.WritableFont + */ + public Font(FontName fn, int ps) + { + super(fn, ps); + } + + /** + * Creates a font of the specified face, point size and bold style + * + * @param ps the point size + * @param bs the bold style + * @param fn the font name + * @deprecated use jxl.write.WritableFont + */ + public Font(FontName fn, int ps, BoldStyle bs) + { + super(fn, ps, bs); + } + + /** + * Creates a font of the specified face, point size, bold weight and + * italicised option. + * + * @param ps the point size + * @param bs the bold style + * @param italic italic flag + * @param fn the font name + * @deprecated use jxl.write.WritableFont + */ + public Font(FontName fn, int ps, BoldStyle bs, boolean italic) + { + super(fn, ps, bs, italic); + } + + /** + * Creates a font of the specified face, point size, bold weight, + * italicisation and underline style + * + * @param ps the point size + * @param bs the bold style + * @param us underscore flag + * @param fn font name + * @param it italic flag + * @deprecated use jxl.write.WritableFont + */ + public Font(FontName fn, + int ps, + BoldStyle bs, + boolean it, + UnderlineStyle us) + { + super(fn, ps, bs, it, us); + } + + + /** + * Creates a font of the specified face, point size, bold style, + * italicisation, underline style and colour + * + * @param ps the point size + * @param bs the bold style + * @param us the underline style + * @param fn the font name + * @param it italic flag + * @param c the colour + * @deprecated use jxl.write.WritableFont + */ + public Font(FontName fn, + int ps, + BoldStyle bs, + boolean it, + UnderlineStyle us, + Colour c) + { + super(fn, ps, bs, it, us, c); + } + + + /** + * Creates a font of the specified face, point size, bold style, + * italicisation, underline style, colour, and script + * style (superscript/subscript) + * + * @param ps the point size + * @param bs the bold style + * @param us the underline style + * @param fn the font name + * @param it the italic flag + * @param c the colour + * @param ss the script style + * @deprecated use jxl.write.WritableFont + */ + public Font(FontName fn, + int ps, + BoldStyle bs, + boolean it, + UnderlineStyle us, + Colour c, + ScriptStyle ss) + { + super(fn, ps, bs, it, us, c, ss); + } +} + Index: 3rdParty_sources/jexcelapi/jxl/write/Formula.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/Formula.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/Formula.java 17 Aug 2012 14:51:21 -0000 1.1 @@ -0,0 +1,77 @@ +/********************************************************************* +* +* Copyright (C) 2001 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write; + +import jxl.format.CellFormat; +import jxl.write.biff.FormulaRecord; + +/** + * A cell, created by user applications, which contains a numerical value + */ +public class Formula extends FormulaRecord implements WritableCell +{ + /** + * Constructs the formula + * + * @param c the column + * @param r the row + * @param form the formula + */ + public Formula(int c, int r, String form) + { + super(c, r, form); + } + + /** + * Constructs a formula + * + * @param c the column + * @param r the row + * @param form the formula + * @param st the cell style + */ + public Formula(int c, int r, String form, CellFormat st) + { + super(c, r, form, st); + } + + /** + * Copy constructor + * + * @param c the column + * @param r the row + * @param f the record to copy + */ + protected Formula(int c, int r, Formula f) + { + super(c, r, f); + } + /** + * Implementation of the deep copy function + * + * @param col the column which the new cell will occupy + * @param row the row which the new cell will occupy + * @return a copy of this cell, which can then be added to the sheet + */ + public WritableCell copyTo(int col, int row) + { + return new Formula(col, row, this); + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/Label.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/Label.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/Label.java 17 Aug 2012 14:51:22 -0000 1.1 @@ -0,0 +1,104 @@ +/********************************************************************* +* +* Copyright (C) 2001 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write; + +import jxl.LabelCell; +import jxl.format.CellFormat; +import jxl.write.biff.LabelRecord; + +/** + * A cell containing text which may be created by user applications + */ +public class Label extends LabelRecord implements WritableCell, LabelCell +{ + /** + * Creates a cell which, when added to the sheet, will be presented at the + * specified column and row co-ordinates and will contain the specified text + * + * @param c the column + * @param cont the text + * @param r the row + */ + public Label(int c, int r, String cont) + { + super(c, r, cont); + } + + /** + * Creates a cell which, when added to the sheet, will be presented at the + * specified column and row co-ordinates and will present the specified text + * in the manner specified by the CellFormat parameter + * + * @param c the column + * @param cont the data + * @param r the row + * @param st the cell format + */ + public Label(int c, int r, String cont, CellFormat st) + { + super(c, r, cont, st); + } + + /** + * Copy constructor used for deep copying + * + * @param col the column + * @param row the row + * @param l the label to copy + */ + protected Label(int col, int row, Label l) + { + super(col, row, l); + } + + /** + * Constructor used internally by the application when making a writable + * copy of a spreadsheet being read in + * + * @param lc the label to copy + */ + public Label(LabelCell lc) + { + super(lc); + } + + /** + * Sets the string contents of this cell + * + * @param s the new data + */ + public void setString(String s) + { + super.setString(s); + } + + /** + * Implementation of the deep copy function + * + * @param col the column which the new cell will occupy + * @param row the row which the new cell will occupy + * @return a copy of this cell, which can then be added to the sheet + */ + public WritableCell copyTo(int col, int row) + { + return new Label(col, row, this); + } +} + Index: 3rdParty_sources/jexcelapi/jxl/write/Number.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/Number.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/Number.java 17 Aug 2012 14:51:21 -0000 1.1 @@ -0,0 +1,106 @@ +/********************************************************************* +* +* Copyright (C) 2001 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write; + +import jxl.NumberCell; +import jxl.format.CellFormat; +import jxl.write.biff.NumberRecord; + +/** + * A cell, created by user applications, which contains a numerical value + */ +public class Number extends NumberRecord implements WritableCell, NumberCell +{ + /** + * Constructs a number, which, when added to a spreadsheet, will display the + * specified value at the column/row position indicated. By default, the + * cell will display with an accuracy of 3 decimal places + * + * @param c the column + * @param r the row + * @param val the value + */ + public Number(int c, int r, double val) + { + super(c, r, val); + } + + /** + * Constructs a number, which, when added to a spreadsheet, will display the + * specified value at the column/row position with the specified CellFormat. + * The CellFormat may specify font information and number format information + * such as the number of decimal places + * + * @param c the column + * @param r the row + * @param val the value + * @param st the cell format + */ + public Number(int c, int r, double val, CellFormat st) + { + super(c, r, val, st); + } + + /** + * Constructor used internally by the application when making a writable + * copy of a spreadsheet that has been read in + * + * @param nc the cell to copy + */ + public Number(NumberCell nc) + { + super(nc); + } + + /** + * Sets the numerical value for this cell + * + * @param val the value + */ + public void setValue(double val) + { + super.setValue(val); + } + + /** + * Copy constructor used for deep copying + * + * @param col the column + * @param row the row + * @param n the number to copy + */ + protected Number(int col, int row, Number n) + { + super(col, row, n); + } + + /** + * Implementation of the deep copy function + * + * @param col the column which the new cell will occupy + * @param row the row which the new cell will occupy + * @return a copy of this cell, which can then be added to the sheet + */ + public WritableCell copyTo(int col, int row) + { + return new Number(col, row, this); + } + +} Index: 3rdParty_sources/jexcelapi/jxl/write/NumberFormat.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/NumberFormat.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/NumberFormat.java 17 Aug 2012 14:51:22 -0000 1.1 @@ -0,0 +1,139 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write; + +import java.text.DecimalFormat; + +import jxl.biff.DisplayFormat; +import jxl.write.biff.NumberFormatRecord; + +/** + * A custom user defined number format, which may be instantiated within user + * applications in order to present numerical values to the appropriate level + * of accuracy. + * The string format used to create a number format adheres to the standard + * java specification, and JExcelAPI makes the necessary modifications so + * that it is rendered in Excel as the nearest possible equivalent. + * Once created, this may be used within a CellFormat object, which in turn + * is a parameter passed to the constructor of the Number cell + */ +public class NumberFormat extends NumberFormatRecord implements DisplayFormat +{ + /** + * Pass in to the constructor to bypass the format validation + */ + public static final NonValidatingFormat COMPLEX_FORMAT = + new NumberFormatRecord.NonValidatingFormat(); + + // Some format strings + + /** + * Constant format string for the Euro currency symbol where it precedes + * the format + */ + public static final String CURRENCY_EURO_PREFIX = "[$�-2]"; + + /** + * Constant format string for the Euro currency symbol where it precedes + * the format + */ + public static final String CURRENCY_EURO_SUFFIX = "[$�-1]"; + + /** + * Constant format string for the UK pound sign + */ + public static final String CURRENCY_POUND = "�"; + + /** + * Constant format string for the Japanese Yen sign + */ + public static final String CURRENCY_JAPANESE_YEN = "[$�-411]"; + + /** + * Constant format string for the US Dollar sign + */ + public static final String CURRENCY_DOLLAR = "[$$-409]"; + + /** + * Constant format string for three digit fractions + */ + public static final String FRACTION_THREE_DIGITS = "???/???"; + + /** + * Constant format string for fractions as halves + */ + public static final String FRACTION_HALVES = "?/2"; + + /** + * Constant format string for fractions as quarter + */ + public static final String FRACTION_QUARTERS = "?/4"; + + /** + * Constant format string for fractions as eighths + */ + public static final String FRACTIONS_EIGHTHS = "?/8"; + + /** + * Constant format string for fractions as sixteenths + */ + public static final String FRACTION_SIXTEENTHS = "?/16"; + + /** + * Constant format string for fractions as tenths + */ + public static final String FRACTION_TENTHS = "?/10"; + + /** + * Constant format string for fractions as hundredths + */ + public static final String FRACTION_HUNDREDTHS = "?/100"; + + /** + * Constructor, taking in the Java compliant number format + * + * @param format the format string + */ + public NumberFormat(String format) + { + super(format); + + // Verify that the format is valid + DecimalFormat df = new DecimalFormat(format); + } + + /** + * Constructor, taking in the non-Java compliant number format. This + * may be used for currencies and more complex custom formats, which + * will not be subject to the standard validation rules. + * As there is no validation, there is a resultant risk that the + * generated Excel file will be corrupt + * + * USE THIS CONSTRUCTOR ONLY IF YOU ARE CERTAIN THAT THE NUMBER FORMAT + * YOU ARE USING IS EXCEL COMPLIANT + * + * @param format the format string + * @param dummy dummy parameter + */ + public NumberFormat(String format, NonValidatingFormat dummy) + { + super(format, dummy); + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/NumberFormats.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/NumberFormats.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/NumberFormats.java 17 Aug 2012 14:51:21 -0000 1.1 @@ -0,0 +1,304 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write; + +import jxl.biff.DisplayFormat; +import jxl.format.Format; + +/** + * Static class which contains the available list of built in Number formats + */ +public final class NumberFormats +{ + /** + * Inner class which holds the format index + */ + private static class BuiltInFormat implements DisplayFormat, Format + { + /** + * The built in number format index + */ + private int index; + + /** + * The format string + */ + private String formatString; + + /** + * Constructor, using the predetermined index + * + * @param i the index + * @param s the string + */ + public BuiltInFormat(int i, String s) + { + index = i; + formatString = s; + } + + /** + * Accessor for the format index + * + * @return the index + */ + public int getFormatIndex() + { + return index; + } + /** + * Accessor to determine if this format has been initialized. Since it is + * built in, this will always return TRUE + * + * @return TRUE, since this is a built in format + */ + public boolean isInitialized() + { + return true; + } + /** + * Determines whether this format is a built in format + * + * @return TRUE, since this is a built in numerical format + */ + public boolean isBuiltIn() + { + return true; + } + /** + * Initializes this format with a dynamically determined index value. + * Since this is a built in, and hence the index value is predetermined, + * this method has an empty body + * + * @param pos the pos in the number formats list + */ + public void initialize(int pos) + { + } + /** + * Accesses the excel format string which is applied to the cell + * Note that this is the string that excel uses, and not the java + * equivalent + * + * @return the cell format string + */ + public String getFormatString() + { + return formatString; + } + + /** + * Standard equals method + * + * @param o the object to compare + * @return TRUE if the two objects are equal, FALSE otherwise + */ + public boolean equals(Object o) + { + if (o == this) + { + return true; + } + + if (!(o instanceof BuiltInFormat)) + { + return false; + } + + BuiltInFormat bif = (BuiltInFormat) o; + + return index == bif.index; + } + + /** + * Standard hash code method + * + * @return the hash code + */ + public int hashCode() + { + return index; + } + } + + + // The available built in number formats + // First describe the fairly bog standard formats + + /** + * The default format. This is equivalent to a number format of '#' + */ + public static final DisplayFormat DEFAULT = new BuiltInFormat(0x0, "#"); + /** + * Formatting for an integer number. This is equivalent to a DecimalFormat + * of "0" + */ + public static final DisplayFormat INTEGER = new BuiltInFormat(0x1, "0"); + + /** + * Formatting for a float. This formats number to two decimal places. It + * is equivalent to a DecimalFormat of "0.00" + */ + public static final DisplayFormat FLOAT = new BuiltInFormat(0x2, "0.00"); + + /** + * Formatting for an integer that has a thousands separator. + * Equivalent to a DecimalFormat of "#,##0" + */ + public static final DisplayFormat THOUSANDS_INTEGER = + new BuiltInFormat(0x3, "#,##0"); + + /** + * Formatting for a float that has a thousands separator. + * Equivalent to a DecimalFormat of "#,##0.00" + */ + public static final DisplayFormat THOUSANDS_FLOAT = + new BuiltInFormat(0x4, "#,##0.00"); + + /** + * Formatting for an integer which is presented in accounting format + * (ie. deficits appear in parentheses) + * Equivalent to a DecimalFormat of "$#,##0;($#,##0)" + */ + public static final DisplayFormat ACCOUNTING_INTEGER = + new BuiltInFormat(0x5, "$#,##0;($#,##0)"); + + /** + * As ACCOUNTING_INTEGER except that deficits appear coloured red + */ + public static final DisplayFormat ACCOUNTING_RED_INTEGER = + new BuiltInFormat(0x6, "$#,##0;($#,##0)"); + + /** + * Formatting for an integer which is presented in accounting format + * (ie. deficits appear in parentheses) + * Equivalent to a DecimalFormat of "$#,##0;($#,##0)" + */ + public static final DisplayFormat ACCOUNTING_FLOAT = + new BuiltInFormat(0x7, "$#,##0;($#,##0)"); + + /** + * As ACCOUNTING_FLOAT except that deficits appear coloured red + */ + public static final DisplayFormat ACCOUNTING_RED_FLOAT = + new BuiltInFormat(0x8, "$#,##0;($#,##0)"); + + /** + * Formatting for an integer presented as a percentage + * Equivalent to a DecimalFormat of "0%" + */ + public static final DisplayFormat PERCENT_INTEGER = + new BuiltInFormat(0x9, "0%"); + + /** + * Formatting for a float percentage + * Equivalent to a DecimalFormat "0.00%" + */ + public static final DisplayFormat PERCENT_FLOAT = + new BuiltInFormat(0xa, "0.00%"); + + /** + * Formatting for exponential or scientific notation + * Equivalent to a DecimalFormat "0.00E00" + */ + public static final DisplayFormat EXPONENTIAL = + new BuiltInFormat(0xb, "0.00E00"); + + /** + * Formatting for one digit fractions + */ + public static final DisplayFormat FRACTION_ONE_DIGIT = + new BuiltInFormat(0xc,"?/?"); + + /** + * Formatting for two digit fractions + */ + public static final DisplayFormat FRACTION_TWO_DIGITS = + new BuiltInFormat(0xd,"??/??"); + + // Now describe the more obscure formats + + /** + * Equivalent to a DecimalFormat "#,##0;(#,##0)" + */ + public static final DisplayFormat FORMAT1 = + new BuiltInFormat(0x25, "#,##0;(#,##0)"); + + /** + * Equivalent to FORMAT1 except deficits are coloured red + */ + public static final DisplayFormat FORMAT2 = + new BuiltInFormat(0x26, "#,##0;(#,##0)"); + + /** + * Equivalent to DecimalFormat "#,##0.00;(#,##0.00)" + */ + public static final DisplayFormat FORMAT3 = + new BuiltInFormat(0x27, "#,##0.00;(#,##0.00)"); + + /** + * Equivalent to FORMAT3 except deficits are coloured red + */ + public static final DisplayFormat FORMAT4 = + new BuiltInFormat(0x28, "#,##0.00;(#,##0.00)"); + + /** + * Equivalent to DecimalFormat "#,##0;(#,##0)" + */ + public static final DisplayFormat FORMAT5 = + new BuiltInFormat(0x29, "#,##0;(#,##0)"); + + /** + * Equivalent to FORMAT5 except deficits are coloured red + */ + public static final DisplayFormat FORMAT6 = + new BuiltInFormat(0x2a, "#,##0;(#,##0)"); + + /** + * Equivalent to DecimalFormat "#,##0.00;(#,##0.00)" + */ + public static final DisplayFormat FORMAT7 = + new BuiltInFormat(0x2b, "#,##0.00;(#,##0.00)"); + + /** + * Equivalent to FORMAT7 except deficits are coloured red + */ + public static final DisplayFormat FORMAT8 = + new BuiltInFormat(0x2c, "#,##0.00;(#,##0.00)"); + + /** + * Equivalent to FORMAT7 + */ + public static final DisplayFormat FORMAT9 = + new BuiltInFormat(0x2e, "#,##0.00;(#,##0.00)"); + + /** + * Equivalent to DecimalFormat "##0.0E0" + */ + public static final DisplayFormat FORMAT10 = + new BuiltInFormat(0x30, "##0.0E0"); + + /** + * Forces numbers to be interpreted as text + */ + public static final DisplayFormat TEXT = new BuiltInFormat(0x31, "@"); +} + + Index: 3rdParty_sources/jexcelapi/jxl/write/Pattern.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/Pattern.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/Pattern.java 17 Aug 2012 14:51:22 -0000 1.1 @@ -0,0 +1,50 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write; + +/** + * Enumeration class which contains the various patterns available within + * the standard Excel pattern palette + * + * @deprecated Repackaged as jxl.format.Pattern + */ +public final class Pattern extends jxl.format.Pattern +{ + /** + * Private constructor + * + * @param val + */ + private Pattern() + { + super(0, null); + } +} + + + + + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/write/VerticalAlignment.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/VerticalAlignment.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/VerticalAlignment.java 17 Aug 2012 14:51:21 -0000 1.1 @@ -0,0 +1,37 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write; + +/** + * Enumeration type which describes the vertical alignment of data within a cell + * + * @deprecated Repackaged as jxl.format.VerticalAlignment + */ +public final class VerticalAlignment extends jxl.format.VerticalAlignment +{ + /** + * Constructor + */ + private VerticalAlignment() + { + super(0, null); + } +} + Index: 3rdParty_sources/jexcelapi/jxl/write/WritableCell.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/WritableCell.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/WritableCell.java 17 Aug 2012 14:51:22 -0000 1.1 @@ -0,0 +1,62 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write; + +import jxl.Cell; +import jxl.format.CellFormat; + +/** + * The interface for all writable cells + */ +public interface WritableCell extends Cell +{ + /** + * Sets the cell format for this cell + * + * @param cf the cell format + */ + public void setCellFormat(CellFormat cf); + + /** + * A deep copy. The returned cell still needs to be added to the sheet. + * By not automatically adding the cell to the sheet, the client program + * may change certain attributes, such as the value or the format + * + * @param col the column which the new cell will occupy + * @param row the row which the new cell will occupy + * @return a copy of this cell, which can then be added to the sheet + */ + public WritableCell copyTo(int col, int row); + + /** + * Accessor for the cell features + * + * @return the cell features or NULL if this cell doesn't have any + */ + public WritableCellFeatures getWritableCellFeatures(); + + /** + * Sets the cell features + * + * @param cf the cell features + */ + public void setCellFeatures(WritableCellFeatures cf); +} + Index: 3rdParty_sources/jexcelapi/jxl/write/WritableCellFeatures.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/WritableCellFeatures.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/WritableCellFeatures.java 17 Aug 2012 14:51:22 -0000 1.1 @@ -0,0 +1,168 @@ +/********************************************************************* +* +* Copyright (C) 2004 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write; + +import java.util.Collection; + +import jxl.CellFeatures; +import jxl.biff.BaseCellFeatures; + +/** + * Container for any additional cell features + */ +public class WritableCellFeatures extends CellFeatures +{ + // shadow the conditions in the base class so that they appear on + // the public generated javadoc + public static final ValidationCondition BETWEEN = BaseCellFeatures.BETWEEN; + public static final ValidationCondition NOT_BETWEEN = + BaseCellFeatures.NOT_BETWEEN; + public static final ValidationCondition EQUAL = BaseCellFeatures.EQUAL; + public static final ValidationCondition NOT_EQUAL = + BaseCellFeatures.NOT_EQUAL; + public static final ValidationCondition GREATER_THAN = + BaseCellFeatures.GREATER_THAN; + public static final ValidationCondition LESS_THAN = + BaseCellFeatures.LESS_THAN; + public static final ValidationCondition GREATER_EQUAL = + BaseCellFeatures.GREATER_EQUAL; + public static final ValidationCondition LESS_EQUAL = + BaseCellFeatures.LESS_EQUAL; + + /** + * Constructor + */ + public WritableCellFeatures() + { + super(); + } + + /** + * Copy constructor + * + * @param cf the cell to copy + */ + public WritableCellFeatures(CellFeatures cf) + { + super(cf); + } + + /** + * Sets the cell comment + * + * @param s the comment + */ + public void setComment(String s) + { + super.setComment(s); + } + + /** + * Sets the cell comment and sets the size of the text box (in cells) + * in which the comment is displayed + * + * @param s the comment + * @param width the width of the comment box in cells + * @param height the height of the comment box in cells + */ + public void setComment(String s, double width, double height) + { + super.setComment(s, width, height); + } + + /** + * Removes the cell comment, if present + */ + public void removeComment() + { + super.removeComment(); + } + + + /** + * Removes any data validation, if present + */ + public void removeDataValidation() + { + super.removeDataValidation(); + } + + /** + * The list of items to validate for this cell. For each object in the + * collection, the toString() method will be called and the data entered + * will be validated against that string + * + * @param c the list of valid values + */ + public void setDataValidationList(Collection c) + { + super.setDataValidationList(c); + } + + /** + * The list of items to validate for this cell in the form of a cell range. + * + * @param col1 the first column containing the data to validate against + * @param row1 the first row containing the data to validate against + * @param col2 the second column containing the data to validate against + * @param row2 the second row containing the data to validate against + */ + public void setDataValidationRange(int col1, int row1, int col2, int row2) + { + super.setDataValidationRange(col1, row1, col2, row2); + } + + /** + * Sets the data validation based upon a named range + * + * @param namedRange the workbook named range defining the validation + * boundaries + */ + public void setDataValidationRange(String namedRange) + { + super.setDataValidationRange(namedRange); + } + + + /** + * Sets the numeric value against which to validate + * + * @param val the number + * @param c the validation condition + */ + public void setNumberValidation(double val, ValidationCondition c) + { + super.setNumberValidation(val, c); + } + + /** + * Sets the numeric range against which to validate the data + * + * @param val1 the first number + * @param val2 the second number + * @param c the validation condition + */ + public void setNumberValidation(double val1, + double val2, + ValidationCondition c) + { + super.setNumberValidation(val1, val2, c); + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/WritableCellFormat.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/WritableCellFormat.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/WritableCellFormat.java 17 Aug 2012 14:51:21 -0000 1.1 @@ -0,0 +1,235 @@ +/********************************************************************* +* +* Copyright (C) 2001 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write; + +import jxl.biff.DisplayFormat; +import jxl.format.Alignment; +import jxl.format.Border; +import jxl.format.BorderLineStyle; +import jxl.format.CellFormat; +import jxl.format.Colour; +import jxl.format.Orientation; +import jxl.format.Pattern; +import jxl.format.VerticalAlignment; +import jxl.write.biff.CellXFRecord; + +/** + * A user specified cell format, which may be reused across many cells. + * The constructors takes parameters, such as font details and the numerical + * date formats, which specify to Excel how cells with this format should be + * displayed. + * Once a CellFormat has been added to a Cell which has been added to + * a sheet, then the CellFormat becomes immutable (to prevent unforeseen + * effects on other cells which share the same format). Attempts to + * call the various set... functions on a WritableCellFormat after this + * time will result in a runtime exception. + */ +public class WritableCellFormat extends CellXFRecord +{ + /** + * A default constructor, which uses the default font and format. + * This constructor should be used in conjunction with the more + * advanced two-phase methods setAlignment, setBorder etc. + */ + public WritableCellFormat() + { + this(WritableWorkbook.ARIAL_10_PT, NumberFormats.DEFAULT); + } + + /** + * A CellFormat which specifies the font for cells with this format + * + * @param font the font + */ + public WritableCellFormat(WritableFont font) + { + this(font, NumberFormats.DEFAULT); + } + + /** + * A constructor which specifies a date/number format for Cells which + * use this format object + * + * @param format the format + */ + public WritableCellFormat(DisplayFormat format) + { + this(WritableWorkbook.ARIAL_10_PT, format); + } + + /** + * A constructor which specifies the font and date/number format for cells + * which wish to use this format + * + * @param font the font + * @param format the date/number format + */ + public WritableCellFormat(WritableFont font, DisplayFormat format) + { + super(font, format); + } + + /** + * A public copy constructor which can be used for copy formats between + * different sheets + * @param format the cell format to copy + */ + public WritableCellFormat(CellFormat format) + { + super(format); + } + + /** + * Sets the horizontal alignment for this format + * + * @param a the alignment + * @exception WriteException + */ + public void setAlignment(Alignment a) throws WriteException + { + super.setAlignment(a); + } + + /** + * Sets the vertical alignment for this format + * + * @param va the vertical alignment + * @exception WriteException + */ + public void setVerticalAlignment(VerticalAlignment va) throws WriteException + { + super.setVerticalAlignment(va); + } + + /** + * Sets the text orientation for this format + * + * @param o the orientation + * @exception WriteException + */ + public void setOrientation(Orientation o) throws WriteException + { + super.setOrientation(o); + } + + /** + * Sets the wrap indicator for this format. If the wrap is set to TRUE, then + * Excel will wrap data in cells with this format so that it fits within the + * cell boundaries + * + * @param w the wrap flag + * @exception jxl.write.WriteException + */ + public void setWrap(boolean w) throws WriteException + { + super.setWrap(w); + } + + /** + * Sets the specified border for this format + * + * @param b the border + * @param ls the border line style + * @exception jxl.write.WriteException + */ + public void setBorder(Border b, BorderLineStyle ls) throws WriteException + { + super.setBorder(b, ls, Colour.BLACK); + } + + /** + * Sets the specified border for this format + * + * @param b the border + * @param ls the border line style + * @param c the colour of the specified border + * @exception jxl.write.WriteException + */ + public void setBorder(Border b, BorderLineStyle ls, Colour c) + throws WriteException + { + super.setBorder(b, ls, c); + } + + /** + * Sets the background colour for this cell format + * + * @param c the bacground colour + * @exception jxl.write.WriteException + */ + public void setBackground(Colour c) throws WriteException + { + this.setBackground(c, Pattern.SOLID); + } + + /** + * Sets the background colour and pattern for this cell format + * + * @param c the colour + * @param p the pattern + * @exception jxl.write.WriteException + */ + public void setBackground(Colour c, Pattern p) throws WriteException + { + super.setBackground(c, p); + } + + /** + * Sets the shrink to fit flag + * + * @param s shrink to fit flag + * @exception WriteException + */ + public void setShrinkToFit(boolean s) throws WriteException + { + super.setShrinkToFit(s); + } + + /** + * Sets the indentation of the cell text + * + * @param i the indentation + */ + public void setIndentation(int i) throws WriteException + { + super.setIndentation(i); + } + + + /** + * Sets whether or not this XF record locks the cell. For this to + * have any effect, the sheet containing cells with this format must + * also be locke3d + * + * @param l the locked flag + * @exception WriteException + */ + public void setLocked(boolean l) throws WriteException + { + super.setLocked(l); + } + +} + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/write/WritableFont.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/WritableFont.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/WritableFont.java 17 Aug 2012 14:51:22 -0000 1.1 @@ -0,0 +1,360 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write; + +import jxl.format.Colour; +import jxl.format.Font; +import jxl.format.ScriptStyle; +import jxl.format.UnderlineStyle; +import jxl.write.biff.WritableFontRecord; + +/** + * A class which is instantiated when the user application wishes to specify + * the font for a particular cell + */ +public class WritableFont extends WritableFontRecord +{ + /** + * Static inner class used for classifying the font names + */ + public static class FontName + { + /** + * The name + */ + String name; + + /** + * Constructor + * + * @param s the font name + */ + FontName(String s) + { + name = s; + } + } + + /** + * Static inner class used for the boldness of the fonts + */ + /*private*/ static class BoldStyle + { + /** + * The value + */ + public int value; + + /** + * Constructor + * + * @param val the value + */ + BoldStyle(int val) + { + value = val; + } + } + + /** + * Objects created with this font name will be rendered within Excel as ARIAL + * fonts + */ + public static final FontName ARIAL = new FontName("Arial"); + /** + * Objects created with this font name will be rendered within Excel as TIMES + * fonts + */ + public static final FontName TIMES = new FontName("Times New Roman"); + /** + * Objects created with this font name will be rendered within Excel as + * COURIER fonts + */ + public static final FontName COURIER = new FontName("Courier New"); + /** + * Objects created with this font name will be rendered within Excel as + * TAHOMA fonts + */ + public static final FontName TAHOMA = new FontName("Tahoma"); + + // The bold styles + + /** + * Indicates that this font should not be presented as bold + */ + public static final BoldStyle NO_BOLD = new BoldStyle(0x190); + /** + * Indicates that this font should be presented in a BOLD style + */ + public static final BoldStyle BOLD = new BoldStyle(0x2bc); + + /** + * The default point size for all Fonts + */ + public static final int DEFAULT_POINT_SIZE = 10; + + /** + * Creates a default font, vanilla font of the specified face and with + * default point size. + * + * @param fn the font name + */ + public WritableFont(FontName fn) + { + this(fn, + DEFAULT_POINT_SIZE, + NO_BOLD, + false, + UnderlineStyle.NO_UNDERLINE, + Colour.BLACK, + ScriptStyle.NORMAL_SCRIPT); + } + + /** + * Publicly available copy constructor + * + * @param f the font to copy + */ + public WritableFont(Font f) + { + super(f); + } + + /** + * Constructs of font of the specified face and of size given by the + * specified point size + * + * @param ps the point size + * @param fn the font name + */ + public WritableFont(FontName fn, int ps) + { + this(fn, ps, NO_BOLD, false, + UnderlineStyle.NO_UNDERLINE, + Colour.BLACK, + ScriptStyle.NORMAL_SCRIPT); + } + + /** + * Creates a font of the specified face, point size and bold style + * + * @param ps the point size + * @param bs the bold style + * @param fn the font name + */ + public WritableFont(FontName fn, int ps, BoldStyle bs) + { + this(fn, ps, bs, false, + UnderlineStyle.NO_UNDERLINE, + Colour.BLACK, + ScriptStyle.NORMAL_SCRIPT); + } + + /** + * Creates a font of the specified face, point size, bold weight and + * italicised option. + * + * @param ps the point size + * @param bs the bold style + * @param italic italic flag + * @param fn the font name + */ + public WritableFont(FontName fn, int ps, BoldStyle bs, boolean italic) + { + this(fn, ps, bs, italic, + UnderlineStyle.NO_UNDERLINE, + Colour.BLACK, + ScriptStyle.NORMAL_SCRIPT); + } + + /** + * Creates a font of the specified face, point size, bold weight, + * italicisation and underline style + * + * @param ps the point size + * @param bs the bold style + * @param us the underline style + * @param fn the font name + * @param it italic flag + */ + public WritableFont(FontName fn, + int ps, + BoldStyle bs, + boolean it, + UnderlineStyle us) + { + this(fn, ps, bs, it, us, Colour.BLACK, ScriptStyle.NORMAL_SCRIPT); + } + + + /** + * Creates a font of the specified face, point size, bold style, + * italicisation, underline style and colour + * + * @param ps the point size + * @param bs the bold style + * @param us the underline style + * @param fn the font name + * @param it italic flag + * @param c the colour + */ + public WritableFont(FontName fn, + int ps, + BoldStyle bs, + boolean it, + UnderlineStyle us, + Colour c) + { + this(fn, ps, bs, it, us, c, ScriptStyle.NORMAL_SCRIPT); + } + + + /** + * Creates a font of the specified face, point size, bold style, + * italicisation, underline style, colour, and script + * style (superscript/subscript) + * + * @param ps the point size + * @param bs the bold style + * @param us the underline style + * @param fn the font name + * @param it the italic flag + * @param c the colour + * @param ss the script style + */ + public WritableFont(FontName fn, + int ps, + BoldStyle bs, + boolean it, + UnderlineStyle us, + Colour c, + ScriptStyle ss) + { + super(fn.name, ps, bs.value, it, + us.getValue(), + c.getValue(), ss.getValue()); + } + + /** + * Sets the point size for this font, if the font hasn't been initialized + * + * @param pointSize the point size + * @exception WriteException, if this font is already in use elsewhere + */ + public void setPointSize(int pointSize) throws WriteException + { + super.setPointSize(pointSize); + } + + /** + * Sets the bold style for this font, if the font hasn't been initialized + * + * @param boldStyle the bold style + * @exception WriteException, if this font is already in use elsewhere + */ + public void setBoldStyle(BoldStyle boldStyle) throws WriteException + { + super.setBoldStyle(boldStyle.value); + } + + /** + * Sets the italic indicator for this font, if the font hasn't been + * initialized + * + * @param italic the italic flag + * @exception WriteException, if this font is already in use elsewhere + */ + public void setItalic(boolean italic) throws WriteException + { + super.setItalic(italic); + } + + /** + * Sets the underline style for this font, if the font hasn't been + * initialized + * + * @param us the underline style + * @exception WriteException, if this font is already in use elsewhere + */ + public void setUnderlineStyle(UnderlineStyle us) throws WriteException + { + super.setUnderlineStyle(us.getValue()); + } + + /** + * Sets the colour for this font, if the font hasn't been + * initialized + * + * @param colour the colour + * @exception WriteException, if this font is already in use elsewhere + */ + public void setColour(Colour colour) throws WriteException + { + super.setColour(colour.getValue()); + } + + /** + * Sets the script style (eg. superscript, subscript) for this font, + * if the font hasn't been initialized + * + * @param scriptStyle the colour + * @exception WriteException, if this font is already in use elsewhere + */ + public void setScriptStyle(ScriptStyle scriptStyle) throws WriteException + { + super.setScriptStyle(scriptStyle.getValue()); + } + + /** + * Accessor for the strike-out flag + * + * @return the strike-out flag + */ + public boolean isStruckout() + { + return super.isStruckout(); + } + + /** + * Sets Accessor for the strike-out flag + * + * @param struckout TRUE if this is a struckout font + * @return the strike-out flag + * @exception WriteException, if this font is already in use elsewhere + */ + public void setStruckout(boolean struckout) throws WriteException + { + super.setStruckout(struckout); + } + + /** + * Factory method which creates the specified font name. This method + * should be used with care, since the string used to create the font + * name must be recognized by Excel's internal processing + * + * @param fontName the name of the Excel font + * @return the font name + */ + public static FontName createFont(String fontName) + { + return new FontName(fontName); + } +} + Index: 3rdParty_sources/jexcelapi/jxl/write/WritableHyperlink.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/WritableHyperlink.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/WritableHyperlink.java 17 Aug 2012 14:51:22 -0000 1.1 @@ -0,0 +1,248 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write; + +import java.io.File; +import java.net.URL; + +import jxl.Hyperlink; +import jxl.write.biff.HyperlinkRecord; + +/** + * A writable hyperlink. Provides API to modify the contents of the hyperlink + */ +public class WritableHyperlink extends HyperlinkRecord implements Hyperlink +{ + /** + * Constructor used internally by the worksheet when making a copy + * of worksheet + * + * @param h the hyperlink being read in + * @param ws the writable sheet containing the hyperlink + */ + public WritableHyperlink(Hyperlink h, WritableSheet ws) + { + super(h, ws); + } + + /** + * Constructs a URL hyperlink in a single cell + * + * @param col the column containing this hyperlink + * @param row the row containing this hyperlink + * @param url the hyperlink + */ + public WritableHyperlink(int col, int row, URL url) + { + this(col, row, col, row, url); + } + + /** + * Constructs a url hyperlink to a range of cells + * + * @param col the column containing this hyperlink + * @param row the row containing this hyperlink + * @param lastcol the last column which activates this hyperlink + * @param lastrow the last row which activates this hyperlink + * @param url the hyperlink + */ + public WritableHyperlink(int col, int row, int lastcol, int lastrow, URL url) + { + this(col, row, lastcol, lastrow, url, null); + } + + /** + * Constructs a url hyperlink to a range of cells + * + * @param col the column containing this hyperlink + * @param row the row containing this hyperlink + * @param lastcol the last column which activates this hyperlink + * @param lastrow the last row which activates this hyperlink + * @param url the hyperlink + * @param desc the description text to place in the cell + */ + public WritableHyperlink(int col, + int row, + int lastcol, + int lastrow, + URL url, + String desc) + { + super(col, row, lastcol, lastrow, url, desc); + } + + /** + * Constructs a file hyperlink in a single cell + * + * @param col the column containing this hyperlink + * @param row the row containing this hyperlink + * @param file the hyperlink + */ + public WritableHyperlink(int col, int row, File file) + { + this(col, row, col, row, file, null); + } + + /** + * Constructs a file hyperlink in a single cell + * + * @param col the column containing this hyperlink + * @param row the row containing this hyperlink + * @param file the hyperlink + * @param desc the hyperlink description + */ + public WritableHyperlink(int col, int row, File file, String desc) + { + this(col, row, col, row, file, desc); + } + + /** + * Constructs a File hyperlink to a range of cells + * + * @param col the column containing this hyperlink + * @param row the row containing this hyperlink + * @param lastcol the last column which activates this hyperlink + * @param lastrow the last row which activates this hyperlink + * @param file the hyperlink + */ + public WritableHyperlink(int col, int row, int lastcol, int lastrow, + File file) + { + super(col, row, lastcol, lastrow, file, null); + } + + /** + * Constructs a File hyperlink to a range of cells + * + * @param col the column containing this hyperlink + * @param row the row containing this hyperlink + * @param lastcol the last column which activates this hyperlink + * @param lastrow the last row which activates this hyperlink + * @param file the hyperlink + * @param desc the description + */ + public WritableHyperlink(int col, + int row, + int lastcol, + int lastrow, + File file, + String desc) + { + super(col, row, lastcol, lastrow, file, desc); + } + + /** + * Constructs a hyperlink to some cells within this workbook + * + * @param col the column containing this hyperlink + * @param row the row containing this hyperlink + * @param desc the cell contents for this hyperlink + * @param sheet the sheet containing the cells to be linked to + * @param destcol the column number of the first destination linked cell + * @param destrow the row number of the first destination linked cell + */ + public WritableHyperlink(int col, int row, + String desc, + WritableSheet sheet, + int destcol, int destrow) + { + this(col, row, col, row, + desc, + sheet, destcol, destrow, destcol, destrow); + } + + /** + * Constructs a hyperlink to some cells within this workbook + * + * @param col the column containing this hyperlink + * @param row the row containing this hyperlink + * @param lastcol the last column which activates this hyperlink + * @param lastrow the last row which activates this hyperlink + * @param desc the cell contents for this hyperlink + * @param sheet the sheet containing the cells to be linked to + * @param destcol the column number of the first destination linked cell + * @param destrow the row number of the first destination linked cell + * @param lastdestcol the column number of the last destination linked cell + * @param lastdestrow the row number of the last destination linked cell + */ + public WritableHyperlink(int col, int row, + int lastcol, int lastrow, + String desc, + WritableSheet sheet, + int destcol, int destrow, + int lastdestcol, int lastdestrow) + { + super(col, row, lastcol, lastrow, + desc, + sheet, destcol, destrow, + lastdestcol, lastdestrow); + } + + /** + * Sets the URL of this hyperlink + * + * @param url the url + */ + public void setURL(URL url) + { + super.setURL(url); + } + + /** + * Sets the file activated by this hyperlink + * + * @param file the file + */ + public void setFile(File file) + { + super.setFile(file); + } + + /** + * Sets the description to appear in the hyperlink cell + * + * @param desc the description + */ + public void setDescription(String desc) + { + super.setContents(desc); + } + + /** + * Sets the location of the cells to be linked to within this workbook + * + * @param desc the label describing the link + * @param sheet the sheet containing the cells to be linked to + * @param destcol the column number of the first destination linked cell + * @param destrow the row number of the first destination linked cell + * @param lastdestcol the column number of the last destination linked cell + * @param lastdestrow the row number of the last destination linked cell + */ + public void setLocation(String desc, + WritableSheet sheet, + int destcol, int destrow, + int lastdestcol, int lastdestrow) + { + super.setLocation(desc, sheet, destcol, destrow, lastdestcol, lastdestrow); + } + +} + + Index: 3rdParty_sources/jexcelapi/jxl/write/WritableImage.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/WritableImage.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/WritableImage.java 17 Aug 2012 14:51:21 -0000 1.1 @@ -0,0 +1,224 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write; + +import java.io.File; + +import jxl.biff.drawing.Drawing; +import jxl.biff.drawing.DrawingGroup; +import jxl.biff.drawing.DrawingGroupObject; + +/** + * Allows an image to be created, or an existing image to be manipulated + * Note that co-ordinates and dimensions are given in cells, so that if for + * example the width or height of a cell which the image spans is altered, + * the image will have a correspondign distortion + */ +public class WritableImage extends Drawing +{ + // Shadow these values from the superclass. The only practical reason + // for doing this is that they appear nicely in the javadoc + + /** + * Image anchor properties which will move and resize an image + * along with the cells + */ + public static ImageAnchorProperties MOVE_AND_SIZE_WITH_CELLS = + Drawing.MOVE_AND_SIZE_WITH_CELLS; + + /** + * Image anchor properties which will move an image + * when cells are inserted or deleted + */ + public static ImageAnchorProperties MOVE_WITH_CELLS = + Drawing.MOVE_WITH_CELLS; + + /** + * Image anchor properties which will leave an image unaffected when + * other cells are inserted, removed or resized + */ + public static ImageAnchorProperties NO_MOVE_OR_SIZE_WITH_CELLS = + Drawing.NO_MOVE_OR_SIZE_WITH_CELLS; + + /** + * Constructor + * + * @param x the column number at which to position the image + * @param y the row number at which to position the image + * @param width the number of columns cells which the image spans + * @param height the number of rows which the image spans + * @param image the source image file + */ + public WritableImage(double x, double y, + double width, double height, + File image) + { + super(x, y, width, height, image); + } + + /** + * Constructor + * + * @param x the column number at which to position the image + * @param y the row number at which to position the image + * @param width the number of columns cells which the image spans + * @param height the number of rows which the image spans + * @param imageData the image data + */ + public WritableImage(double x, + double y, + double width, + double height, + byte[] imageData) + { + super(x, y, width, height, imageData); + } + + /** + * Constructor, used when copying sheets + * + * @param d the image to copy + * @param dg the drawing group + */ + public WritableImage(DrawingGroupObject d, DrawingGroup dg) + { + super(d, dg); + } + + /** + * Accessor for the image position + * + * @return the column number at which the image is positioned + */ + public double getColumn() + { + return super.getX(); + } + + /** + * Accessor for the image position + * + * @param c the column number at which the image should be positioned + */ + public void setColumn(double c) + { + super.setX(c); + } + + /** + * Accessor for the image position + * + * @return the row number at which the image is positions + */ + public double getRow() + { + return super.getY(); + } + + /** + * Accessor for the image position + * + * @param c the row number at which the image should be positioned + */ + public void setRow(double c) + { + super.setY(c); + } + + /** + * Accessor for the image dimensions + * + * @return the number of columns this image spans + */ + public double getWidth() + { + return super.getWidth(); + } + + /** + * Accessor for the image dimensions + * Note that the actual size of the rendered image will depend on the + * width of the columns it spans + * + * @param c the number of columns which this image spans + */ + public void setWidth(double c) + { + super.setWidth(c); + } + + /** + * Accessor for the image dimensions + * + * @return the number of rows which this image spans + */ + public double getHeight() + { + return super.getHeight(); + } + + /** + * Accessor for the image dimensions + * Note that the actual size of the rendered image will depend on the + * height of the rows it spans + * + * @param c the number of rows which this image should span + */ + public void setHeight(double c) + { + super.setHeight(c); + } + + /** + * Accessor for the image file + * + * @return the file which the image references + */ + public File getImageFile() + { + return super.getImageFile(); + } + + /** + * Accessor for the image data + * + * @return the image data + */ + public byte[] getImageData() + { + return super.getImageData(); + } + + /** + * Accessor for the anchor properties + */ + public void setImageAnchor(ImageAnchorProperties iap) + { + super.setImageAnchor(iap); + } + + /** + * Accessor for the anchor properties + */ + public ImageAnchorProperties getImageAnchor() + { + return super.getImageAnchor(); + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/WritableImage.java2 =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/WritableImage.java2,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/WritableImage.java2 17 Aug 2012 14:51:22 -0000 1.1 @@ -0,0 +1,181 @@ +/********************************************************************* +* +* Copyright (C) 2003 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write; + +import java.io.File; + +import jxl.biff.drawing.Drawing; +import jxl.biff.drawing.DrawingGroupObject; +import jxl.biff.drawing.DrawingGroup; + +/** + * Allows an image to be created, or an existing image to be manipulated + * Note that co-ordinates and dimensions are given in cells, so that if for + * example the width or height of a cell which the image spans is altered, + * the image will have a correspondign distortion + */ +public class WritableImage extends Drawing +{ + /** + * Constructor + * + * @param x the column number at which to position the image + * @param y the row number at which to position the image + * @param width the number of columns cells which the image spans + * @param height the number of rows which the image spans + * @param image the source image file + */ + public WritableImage(double x, double y, + double width, double height, + File image) + { + super(x, y, width, height, image); + } + + /** + * Constructor + * + * @param x the column number at which to position the image + * @param y the row number at which to position the image + * @param width the number of columns cells which the image spans + * @param height the number of rows which the image spans + * @param image the image data + */ + public WritableImage(double x, double y, + double width, double height, + byte[] imageData) + { + super(x, y, width, height, imageData); + } + + /** + * Constructor, used when copying sheets + * + * @param d the image to copy + */ + public WritableImage(DrawingGroupObject d, DrawingGroup dg) + { + super(d, dg); + } + + /** + * Accessor for the image position + * + * @return the column number at which the image is positioned + */ + public double getColumn() + { + return super.getX(); + } + + /** + * Accessor for the image position + * + * @param c the column number at which the image should be positioned + */ + public void setColumn(double c) + { + super.setX(c); + } + + /** + * Accessor for the image position + * + * @return the row number at which the image is positions + */ + public double getRow() + { + return super.getY(); + } + + /** + * Accessor for the image position + * + * @param c the row number at which the image should be positioned + */ + public void setRow(double c) + { + super.setY(c); + } + + /** + * Accessor for the image dimensions + * + * @return the number of columns this image spans + */ + public double getWidth() + { + return super.getWidth(); + } + + /** + * Accessor for the image dimensions + * Note that the actual size of the rendered image will depend on the + * width of the columns it spans + * + * @param c the number of columns which this image spans + */ + public void setWidth(double c) + { + super.setWidth(c); + } + + /** + * Accessor for the image dimensions + * + * @return the number of rows which this image spans + */ + public double getHeight() + { + return super.getHeight(); + } + + /** + * Accessor for the image dimensions + * Note that the actual size of the rendered image will depend on the + * height of the rows it spans + * + * @param c the number of rows which this image should span + */ + public void setHeight(double c) + { + super.setHeight(c); + } + + /** + * Accessor for the image file + * + * @return the file which the image references + */ + public File getImageFile() + { + return super.getImageFile(); + } + + /** + * Accessor for the image data + * + * @return the image data + */ + public byte[] getImageData() + { + return super.getImageData(); + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/WritableSheet.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/WritableSheet.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/WritableSheet.java 17 Aug 2012 14:51:22 -0000 1.1 @@ -0,0 +1,409 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write; + +import jxl.CellView; +import jxl.Range; +import jxl.Sheet; +import jxl.format.CellFormat; +import jxl.format.PageOrientation; +import jxl.format.PaperSize; +import jxl.write.biff.RowsExceededException; + +/** + * Interface for a worksheet that may be modified. The most + * important modification for a sheet is to have cells added to it + */ +public interface WritableSheet extends Sheet +{ + /** + * Adds a cell to this sheet + * The RowsExceededException may be caught if client code wishes to + * explicitly trap the case where too many rows have been written + * to the current sheet. If this behaviour is not desired, it is + * sufficient simply to handle the WriteException, since this is a base + * class of RowsExceededException + * + * @param cell the cell to add + * @exception jxl.write..WriteException + * @exception jxl.write.biff.RowsExceededException + */ + public void addCell(WritableCell cell) + throws WriteException, RowsExceededException; + /** + * Sets the name of this sheet + * + * @param name the name of the sheet + */ + public void setName(String name); + /** + * Indicates whether or not this sheet is hidden + * + * @param hidden hidden flag + * @deprecated use the SheetSettings bean instead + */ + public void setHidden(boolean hidden); + /** + * Indicates whether or not this sheet is protected + * + * @param prot Protected flag + * @deprecated use the SheetSettings bean instead + */ + public void setProtected(boolean prot); + + /** + * Sets the width of the column on this sheet, in characters. This causes + * Excel to resize the entire column. + * If the columns specified already has view information associated + * with it, then it is replaced by the new data + * + * @param col the column to be formatted + * @param width the width of the column + */ + public void setColumnView(int col, int width); + + /** + * Sets the width and style of every cell in the specified column. + * If the columns specified already has view information associated + * with it, then it is replaced by the new data + * + * @param col the column to be formatted + * @param format the format of every cell in the column + * @param width the width of the column, in characters + * @deprecated Use the CellView bean instead + */ + public void setColumnView(int col, int width, CellFormat format); + + /** + * Sets the view for this column + * + * @param col the column on which to set the view + * @param view the view to set + */ + public void setColumnView(int col, CellView view); + + /** + * Sets the height of the specified row, as well as its collapse status + * + * @param row the row to be formatted + * @param height the row height in characters + * @exception jxl.write.biff.RowsExceededException + */ + public void setRowView(int row, int height) + throws RowsExceededException; + + /** + * Sets the properties of the specified row + * + * @param row the row to be formatted + * @param collapsed indicates whether the row is collapsed + * @exception jxl.write.biff.RowsExceededException + */ + public void setRowView(int row, boolean collapsed) + throws RowsExceededException; + + /** + * Sets the height of the specified row, as well as its collapse status + * + * @param row the row to be formatted + * @param height the row height in 1/20th of a point + * @param collapsed indicates whether the row is collapsed + * @exception jxl.write.biff.RowsExceededException + */ + public void setRowView(int row, int height, + boolean collapsed) + throws RowsExceededException; + + /** + * Sets the view for this column + * + * @param row the column on which to set the view + * @param view the view to set + * @exception RowsExceededException + */ + public void setRowView(int row, CellView view) throws RowsExceededException; + + /** + * Gets the writable cell from this sheet. Use of this method allows + * the returned cell to be modified by the users application + * + * @param column the column + * @param row the row + * @return the cell at the specified position + */ + public WritableCell getWritableCell(int column, int row); + + /** + * Returns the cell for the specified location eg. "A4". Note that this + * method is identical to calling getCell(CellReferenceHelper.getColumn(loc), + * CellReferenceHelper.getRow(loc)) and its implicit performance + * overhead for string parsing. As such,this method should therefore + * be used sparingly + * + * @param loc the cell reference + * @return the cell at the specified co-ordinates + */ + public WritableCell getWritableCell(String loc); + + /** + * Gets the writable hyperlinks from this sheet. The hyperlinks + * that are returned may be modified by user applications + * + * @return the writable hyperlinks + */ + public WritableHyperlink[] getWritableHyperlinks(); + + /** + * Inserts a blank row into this spreadsheet. If the row is out of range + * of the rows in the sheet, then no action is taken + * + * @param row the row to insert + */ + public void insertRow(int row); + + /** + * Inserts a blank column into this spreadsheet. If the column is out of + * range of the columns in the sheet, then no action is taken + * + * @param col the column to insert + */ + public void insertColumn(int col); + + /** + * Removes a column from this spreadsheet. If the column is out of range + * of the columns in the sheet, then no action is taken + * + * @param col the column to remove + */ + public void removeColumn(int col); + + /** + * Removes a row from this spreadsheet. If the row is out of + * range of the columns in the sheet, then no action is taken + * + * @param row the row to remove + */ + public void removeRow(int row); + + /** + * Merges the specified cells. Any clashes or intersections between + * merged cells are resolved when the spreadsheet is written out + * + * @param col1 the column number of the top left cell + * @param row1 the row number of the top left cell + * @param col2 the column number of the bottom right cell + * @param row2 the row number of the bottom right cell + * @return the Range object representing the merged cells + * @exception jxl.write..WriteException + * @exception jxl.write.biff.RowsExceededException + */ + public Range mergeCells(int col1, int row1, int col2, int row2) + throws WriteException, RowsExceededException; + + /** + * Sets a row grouping + * + * @param row1 the first row of the group + * @param row2 the last row of the group + * @param collapsed should the group be collapsed? + * @exception WriteException + * @exception RowsExceededException + */ + public void setRowGroup(int row1, int row2, boolean collapsed) + throws WriteException, RowsExceededException; + + /** + * Unsets a row grouping + * + * @param row1 the first row to unset + * @param row2 the last row to unset + * @exception WriteException + * @exception RowsExceededException + */ + public void unsetRowGroup(int row1, int row2) + throws WriteException, RowsExceededException; + + /** + * Sets a column grouping + * + * @param col1 the first column of the group + * @param col2 the last column of the group + * @param collapsed should the group be collapsed? + * @exception WriteException + * @exception RowsExceededException + */ + public void setColumnGroup(int col1, int col2, boolean collapsed) + throws WriteException, RowsExceededException; + + /** + * Unsets a column grouping + * + * @param col1 the first column to unset + * @param col2 the last column to unset + * @exception WriteException + * @exception RowsExceededException + */ + public void unsetColumnGroup(int col1, int col2) + throws WriteException, RowsExceededException; + + /** + * Unmerges the specified cells. The Range passed in should be one that + * has been previously returned as a result of the getMergedCells method + * + * @param r the range of cells to unmerge + */ + public void unmergeCells(Range r); + + /** + * Adds the specified hyperlink. Adding a hyperlink causes any populated + * cells in the range of the hyperlink to be set to empty + * If the cells which activate this hyperlink clash with any other cells, + * they are still added to the worksheet and it is left to Excel to + * handle this. + * + * @param h the hyperlink + * @exception jxl.write..WriteException + * @exception jxl.write.biff.RowsExceededException + */ + public void addHyperlink(WritableHyperlink h) + throws WriteException, RowsExceededException;; + + /** + * Removes the specified hyperlink. Note that if you merely set the + * cell contents to be an Empty cell, then the cells containing the + * hyperlink will still be active. The contents of the cell which + * activate the hyperlink are removed. + * The hyperlink passed in must be a hyperlink retrieved using the + * getHyperlinks method + * + * @param h the hyperlink to remove. + */ + public void removeHyperlink(WritableHyperlink h); + + /** + * Removes the specified hyperlink. Note that if you merely set the + * cell contents to be an Empty cell, then the cells containing the + * hyperlink will still be active. + * If the preserveLabel field is set, the cell contents of the + * hyperlink are preserved, although the hyperlink is deactivated. If + * this value is FALSE, the cell contents are removed + * The hyperlink passed in must be a hyperlink retrieved using the + * getHyperlinks method + * + * @param h the hyperlink to remove. + * @param preserveLabel if TRUE preserves the label contents, if FALSE + * removes them + */ + public void removeHyperlink(WritableHyperlink h, boolean preserveLabel); + + /** + * Sets the header for this page + * + * @param l the print header to print on the left side + * @param c the print header to print in the centre + * @param r the print header to print on the right hand side + * @deprecated use the SheetSettings bean + */ + public void setHeader(String l, String c, String r); + + /** + * Sets the footer for this page + * + * @param l the print header to print on the left side + * @param c the print header to print in the centre + * @param r the print header to print on the right hand side + * @deprecated use the SheetSettings bean + */ + public void setFooter(String l, String c, String r); + + /** + * Sets the page setup details + * + * @param p the page orientation + */ + public void setPageSetup(PageOrientation p); + + /** + * Sets the page setup details + * + * @param p the page orientation + * @param hm the header margin, in inches + * @param fm the footer margin, in inches + */ + public void setPageSetup(PageOrientation p, double hm, double fm); + + /** + * Sets the page setup details + * + * @param p the page orientation + * @param ps the paper size + * @param hm the header margin, in inches + * @param fm the footer margin, in inches + */ + public void setPageSetup(PageOrientation p, PaperSize ps, + double hm, double fm); + + /** + * Forces a page break at the specified row + * + * @param row the row to break at + */ + public void addRowPageBreak(int row); + + /** + * Forces a page break at the specified column + * + * @param col the column to break at + */ + public void addColumnPageBreak(int col); + + /** + * Adds an image to the sheet + * + * @param image the image to add + */ + public void addImage(WritableImage image); + + /** + * Accessor for the number of images on the sheet + * + * @return the number of images on this sheet + */ + public int getNumberOfImages(); + + /** + * Accessor for the image + * + * @param i the 0 based image number + * @return the image at the specified position + */ + public WritableImage getImage(int i); + + /** + * Removes the specified image from the sheet. The image passed in + * must be the same instance as that previously retrieved using the + * getImage() method + * + * @param wi the image to remove + */ + public void removeImage(WritableImage wi); +} + + Index: 3rdParty_sources/jexcelapi/jxl/write/WritableWorkbook.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/WritableWorkbook.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/WritableWorkbook.java 17 Aug 2012 14:51:22 -0000 1.1 @@ -0,0 +1,310 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write; + +import java.io.IOException; + +import jxl.Range; +import jxl.Sheet; +import jxl.Workbook; +import jxl.format.Colour; +import jxl.format.UnderlineStyle; + +/** + * A writable workbook + */ +public abstract class WritableWorkbook +{ + // Globally available stuff + + /** + * The default font for Cell formats + */ + public static final WritableFont ARIAL_10_PT = + new WritableFont(WritableFont.ARIAL); + + /** + * The font used for hyperlinks + */ + public static final WritableFont HYPERLINK_FONT = + new WritableFont(WritableFont.ARIAL, + WritableFont.DEFAULT_POINT_SIZE, + WritableFont.NO_BOLD, + false, + UnderlineStyle.SINGLE, + Colour.BLUE); + + /** + * The default style for cells + */ + public static final WritableCellFormat NORMAL_STYLE = + new WritableCellFormat(ARIAL_10_PT, NumberFormats.DEFAULT); + + /** + * The style used for hyperlinks + */ + public static final WritableCellFormat HYPERLINK_STYLE = + new WritableCellFormat(HYPERLINK_FONT); + + /** + * A cell format used to hide the cell contents + */ + public static final WritableCellFormat HIDDEN_STYLE = + new WritableCellFormat(new DateFormat(";;;")); + + /** + * Constructor used by the implemenation class + */ + protected WritableWorkbook() + { + } + + /** + * Gets the sheets within this workbook. Use of this method for + * large worksheets can cause performance problems. + * + * @return an array of the individual sheets + */ + public abstract WritableSheet[] getSheets(); + + /** + * Gets the sheet names + * + * @return an array of strings containing the sheet names + */ + public abstract String[] getSheetNames(); + + /** + * Gets the specified sheet within this workbook + * + * @param index the zero based index of the reQuired sheet + * @return The sheet specified by the index + * @exception IndexOutOfBoundsException when index refers to a non-existent + * sheet + */ + public abstract WritableSheet getSheet(int index) + throws IndexOutOfBoundsException; + + /** + * Gets the sheet with the specified name from within this workbook + * + * @param name the sheet name + * @return The sheet with the specified name, or null if it is not found + */ + public abstract WritableSheet getSheet(String name); + + /** + * Returns the cell for the specified location eg. "Sheet1!A4". + * This is identical to using the CellReferenceHelper with its + * associated performance overheads, consequently it should + * be use sparingly + * + * @param loc the cell to retrieve + * @return the cell at the specified location + */ + public abstract WritableCell getWritableCell(String loc); + + /** + * Returns the number of sheets in this workbook + * + * @return the number of sheets in this workbook + */ + public abstract int getNumberOfSheets(); + + /** + * Closes this workbook, and makes any memory allocated available + * for garbage collection. Also closes the underlying output stream + * if necessary. + * + * @exception IOException + * @exception WriteException + */ + public abstract void close() throws IOException, WriteException; + + /** + * Creates, and returns a worksheet at the specified position + * with the specified name + * If the index specified is less than or equal to zero, the new sheet + * is created at the beginning of the workbook. If the index is greater + * than the number of sheet, then the sheet is created at the + * end of the workbook. + * + * @param name the sheet name + * @param index the index number at which to insert + * @return the new sheet + */ + public abstract WritableSheet createSheet(String name, int index); + + /** + * Imports a sheet from a different workbook. Does a deep copy on all + * elements within that sheet + * + * @param name the name of the new sheet + * @param index the position for the new sheet within this workbook + * @param sheet the sheet (from another workbook) to merge into this one + * @return the new sheet + */ + public abstract WritableSheet importSheet(String name, int index, Sheet s); + + /** + * Copy sheet within the same workbook. The sheet specified is copied to + * the new sheet name at the position + * + * @param s the index of the sheet to copy + * @param name the name of the new sheet + * @param index the position of the new sheet + */ + public abstract void copySheet(int s, String name, int index); + + /** + * Copies the specified sheet and places it at the index + * specified by the parameter + * + * @param s the name of the sheet to copy + * @param name the name of the new sheet + * @param index the position of the new sheet + */ + public abstract void copySheet(String s, String name, int index); + + /** + * Removes the sheet at the specified index from this workbook + * + * @param index the sheet index to remove + */ + public abstract void removeSheet(int index); + + /** + * Moves the specified sheet within this workbook to another index + * position. + * + * @param fromIndex the zero based index of the required sheet + * @param toIndex the zero based index of the required sheet + * @return the sheet that has been moved + */ + public abstract WritableSheet moveSheet(int fromIndex, int toIndex); + + /** + * Writes out the data held in this workbook in Excel format + * + * @exception IOException + */ + public abstract void write() throws IOException; + + /** + * Indicates whether or not this workbook is protected + * + * @param prot Protected flag + */ + public abstract void setProtected(boolean prot); + + /** + * Sets the RGB value for the specified colour for this workbook + * + * @param c the colour whose RGB value is to be overwritten + * @param r the red portion to set (0-255) + * @param g the green portion to set (0-255) + * @param b the blue portion to set (0-255) + */ + public abstract void setColourRGB(Colour c, int r, int g, int b); + + /** + * This method can be used to create a writable clone of some other + * workbook + * + * @param w the workdoock to copy + * @deprecated Copying now occurs implicitly as part of the overloaded + * factory method Workbook.createWorkbood + */ + public void copy(Workbook w) + { + // Was an abstract method - leave the method body blank + } + + /** + * Gets the named cell from this workbook. The name refers to a + * range of cells, then the cell on the top left is returned. If + * the name cannot be, null is returned + * + * @param name the name of the cell/range to search for + * @return the cell in the top left of the range if found, NULL + * otherwise + */ + public abstract WritableCell findCellByName(String name); + + /** + * Gets the named range from this workbook. The Range object returns + * contains all the cells from the top left to the bottom right + * of the range. + * If the named range comprises an adjacent range, + * the Range[] will contain one object; for non-adjacent + * ranges, it is necessary to return an array of length greater than + * one. + * If the named range contains a single cell, the top left and + * bottom right cell will be the same cell + * + * @param name the name of the cell/range to search for + * @return the range of cells + */ + public abstract Range[] findByName(String name); + + /** + * Gets the named ranges + * + * @return the list of named cells within the workbook + */ + public abstract String[] getRangeNames(); + + /** + * Removes the specified named range from the workbook. Note that + * removing a name could cause formulas which use that name to + * calculate their results incorrectly + * + * @param name the name to remove + */ + public abstract void removeRangeName(String name); + + /** + * Add new named area to this workbook with the given information. + * + * @param name name to be created. + * @param sheet sheet containing the name + * @param firstCol first column this name refers to. + * @param firstRow first row this name refers to. + * @param lastCol last column this name refers to. + * @param lastRow last row this name refers to. + */ + public abstract void addNameArea(String name, + WritableSheet sheet, + int firstCol, + int firstRow, + int lastCol, + int lastRow); + + /** + * Sets a new output file. This allows the same workbook to be + * written to various different output files without having to + * read in any templates again + * + * @param fileName the file name + * @exception IOException + */ + public abstract void setOutputFile(java.io.File fileName) + throws IOException; +} Index: 3rdParty_sources/jexcelapi/jxl/write/WriteException.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/WriteException.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/WriteException.java 17 Aug 2012 14:51:22 -0000 1.1 @@ -0,0 +1,38 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write; + +import jxl.JXLException; + +/** + * Exception thrown when using the API to generate an Excel file + */ +public abstract class WriteException extends JXLException +{ + /** + * Constructs this exception with the specified message + * + * @param s the message + */ + protected WriteException(String s) + { + super(s); + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/ArbitraryRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/ArbitraryRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/ArbitraryRecord.java 17 Aug 2012 14:51:09 -0000 1.1 @@ -0,0 +1,73 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import common.Logger; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Writes out some arbitrary record data. Used during the debug process + */ +class ArbitraryRecord extends WritableRecordData +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(ArbitraryRecord.class); + + /** + * The binary data + */ + private byte[] data; + + /** + * Constructor + * + * @param type the biff code + * @param d the data + */ + public ArbitraryRecord(int type, byte[] d) + { + super(Type.createType(type)); + + data = d; + logger.warn("ArbitraryRecord of type " + type + " created"); + } + + /** + * Retrieves the data to be written to the binary file + * + * @return the binary data + */ + public byte[] getData() + { + return data; + } +} + + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/BOFRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/BOFRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/BOFRecord.java 17 Aug 2012 14:51:10 -0000 1.1 @@ -0,0 +1,116 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record to indicate the beginning of a new stream in the Compound + * File + */ +class BOFRecord extends WritableRecordData +{ + /** + * The data to write to the file + */ + private byte[] data; + + // Dummy types for constructor overloading + private static class WorkbookGlobalsBOF{}; + private static class SheetBOF{}; + + public final static WorkbookGlobalsBOF workbookGlobals + = new WorkbookGlobalsBOF(); + public final static SheetBOF sheet = new SheetBOF(); + + /** + * Constructor for generating a workbook globals BOF record + * + * @param dummy - a dummy argument for overloading purposes + */ + public BOFRecord(WorkbookGlobalsBOF dummy) + { + super(Type.BOF); + + // Create the data as biff 8 format with a substream type of + // workbook globals + data = new byte[] + {(byte) 0x0, + (byte) 0x6, + (byte) 0x5, // substream type + (byte) 0x0, // substream type + (byte) 0xf2, // rupBuild + (byte) 0x15, // rupBuild + (byte) 0xcc, // rupYear + (byte) 0x07, // rupYear + (byte) 0x0, // bfh + (byte) 0x0, // bfh + (byte) 0x0, // bfh + (byte) 0x0, // bfh + (byte) 0x6, // sfo + (byte) 0x0, // sfo, + (byte) 0x0, // sfo + (byte) 0x0 // sfo + }; + } + + /** + * Constructor for generating a sheet BOF record + * + * @param dummy - a dummy argument for overloading purposes + */ + public BOFRecord(SheetBOF dummy) + { + super(Type.BOF); + + // Create the data as biff 8 format with a substream type of + // worksheet + data = new byte[] + {(byte) 0x0, + (byte) 0x6, + (byte) 0x10, // substream type + (byte) 0x0, // substream type + (byte) 0xf2, // rupBuild + (byte) 0x15, // rupBuild + (byte) 0xcc, // rupYear + (byte) 0x07, // rupYear + (byte) 0x0, // bfh + (byte) 0x0, // bfh + (byte) 0x0, // bfh + (byte) 0x0, // bfh + (byte) 0x6, // sfo + (byte) 0x0, // sfo, + (byte) 0x0, // sfo + (byte) 0x0 // sfo + }; + } + + /** + * Gets the data for writing to the output file + * + * @return the binary data for writing + */ + public byte[] getData() + { + return data; + } +} + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/BackupRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/BackupRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/BackupRecord.java 17 Aug 2012 14:51:10 -0000 1.1 @@ -0,0 +1,70 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which indicates whether Excel should save backup versions of the + * file + */ +class BackupRecord extends WritableRecordData +{ + /** + * Flag to indicate whether or not Excel should make backups + */ + private boolean backup; + /** + * The data array + */ + private byte[] data; + + /** + * Constructor + * + * @param bu backup flag + */ + public BackupRecord(boolean bu) + { + super(Type.BACKUP); + + backup = bu; + + // Hard code in an unprotected workbook + data = new byte[2]; + + if (backup) + { + IntegerHelper.getTwoBytes(1, data, 0); + } + } + + /** + * Returns the binary data for writing to the output file + * + * @return the binary data + */ + public byte[] getData() + { + return data; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/BlankRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/BlankRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/BlankRecord.java 17 Aug 2012 14:51:12 -0000 1.1 @@ -0,0 +1,109 @@ +/********************************************************************* +* +* Copyright (C) 2001 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import common.Logger; + +import jxl.Cell; +import jxl.CellType; +import jxl.biff.Type; +import jxl.format.CellFormat; + +/** + * A blank record, which is used to contain formatting information + */ +public abstract class BlankRecord extends CellValue +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(BlankRecord.class); + + /** + * Consructor used when creating a label from the user API + * + * @param c the column + * @param cont the contents + * @param r the row + */ + protected BlankRecord(int c, int r) + { + super(Type.BLANK, c, r); + } + + /** + * Constructor used when creating a label from the API. This is + * overloaded to allow formatting information to be passed to the record + * + * @param c the column + * @param r the row + * @param st the format applied to the cell + */ + protected BlankRecord(int c, int r, CellFormat st) + { + super(Type.BLANK, c, r, st); + } + + /** + * Constructor used when copying a formatted blank cell from a read only + * spreadsheet + * + * @param c the blank cell to copy + */ + protected BlankRecord(Cell c) + { + super(Type.BLANK, c); + } + + /** + * Copy constructor + * + * @param c the column + * @param r the row + * @param b the record to copy + */ + protected BlankRecord(int c, int r, BlankRecord br) + { + super(Type.BLANK, c, r, br); + } + + /** + * Returns the content type of this cell + * + * @return the content type for this cell + */ + public CellType getType() + { + return CellType.EMPTY; + } + + /** + * Quick and dirty function to return the contents of this cell as a string. + * + * @return the contents of this cell as a string + */ + public String getContents() + { + return ""; + } +} + + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/BookboolRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/BookboolRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/BookboolRecord.java 17 Aug 2012 14:51:09 -0000 1.1 @@ -0,0 +1,68 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Writes out the workbook option flag (should it save the external + * link options) + */ +class BookboolRecord extends WritableRecordData +{ + /** + * The external link option flag + */ + private boolean externalLink; + /** + * The binary data to write out + */ + private byte[] data; + + /** + * Constructor + * + * @param extlink the external link options flag + */ + public BookboolRecord(boolean extlink) + { + super(Type.BOOKBOOL); + + externalLink = extlink; + data = new byte[2]; + + if (!externalLink) + { + IntegerHelper.getTwoBytes(1, data, 0); + } + } + + /** + * Gets the binary data to write to the output file + * + * @return the binary data + */ + public byte[] getData() + { + return data; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/BooleanRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/BooleanRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/BooleanRecord.java 17 Aug 2012 14:51:13 -0000 1.1 @@ -0,0 +1,161 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + + +import jxl.BooleanCell; +import jxl.CellType; +import jxl.biff.Type; +import jxl.format.CellFormat; + + +/** + * A boolean cell's last calculated value + */ +public abstract class BooleanRecord extends CellValue +{ + /** + * The boolean value of this cell. If this cell represents an error, + * this will be false + */ + private boolean value; + + /** + * Constructor invoked by the user API + * + * @param c the column + * @param r the row + * @param val the value + */ + protected BooleanRecord(int c, int r, boolean val) + { + super(Type.BOOLERR, c, r); + value = val; + } + + /** + * Overloaded constructor invoked from the API, which takes a cell + * format + * + * @param c the column + * @param r the row + * @param val the value + * @param st the cell format + */ + protected BooleanRecord(int c, int r, boolean val, CellFormat st) + { + super(Type.BOOLERR, c, r, st); + value = val; + } + + /** + * Constructor used when copying a workbook + * + * @param nc the number to copy + */ + protected BooleanRecord(BooleanCell nc) + { + super(Type.BOOLERR, nc); + value = nc.getValue(); + } + + /** + * Copy constructor + * + * @param c the column + * @param r the row + * @param br the record to copy + */ + protected BooleanRecord(int c, int r, BooleanRecord br) + { + super(Type.BOOLERR, c, r, br); + value = br.value; + } + + /** + * Interface method which Gets the boolean value stored in this cell. If + * this cell contains an error, then returns FALSE. Always query this cell + * type using the accessor method isError() prior to calling this method + * + * @return TRUE if this cell contains TRUE, FALSE if it contains FALSE or + * an error code + */ + public boolean getValue() + { + return value; + } + + /** + * Returns the numerical value as a string + * + * @return The numerical value of the formula as a string + */ + public String getContents() + { + // return Boolean.toString(value) - only available in 1.4 or later + return (new Boolean(value)).toString(); + } + + /** + * Returns the cell type + * + * @return The cell type + */ + public CellType getType() + { + return CellType.BOOLEAN; + } + + /** + * Sets the value + * + * @param val the boolean value + */ + protected void setValue(boolean val) + { + value = val; + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + byte[] celldata = super.getData(); + byte[] data = new byte[celldata.length + 2]; + System.arraycopy(celldata, 0, data, 0, celldata.length); + + if (value) + { + data[celldata.length] = 1; + } + + return data; + } + +} + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/BottomMarginRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/BottomMarginRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/BottomMarginRecord.java 17 Aug 2012 14:51:08 -0000 1.1 @@ -0,0 +1,33 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; + +/** + * The settings for the left margin + */ +class BottomMarginRecord extends MarginRecord +{ + BottomMarginRecord(double v) + { + super(Type.BOTTOMMARGIN, v); + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/BoundsheetRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/BoundsheetRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/BoundsheetRecord.java 17 Aug 2012 14:51:10 -0000 1.1 @@ -0,0 +1,119 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which stores the sheet name, the sheet type and the stream + * position + */ +class BoundsheetRecord extends WritableRecordData +{ + /** + * Hidden flag + */ + private boolean hidden; + + /** + * Chart only flag + */ + private boolean chartOnly; + + /** + * The name of the sheet + */ + private String name; + + /** + * The data to write to the output file + */ + private byte[] data; + + /** + * Constructor + * + * @param n the sheet name + */ + public BoundsheetRecord(String n) + { + super(Type.BOUNDSHEET); + name = n; + hidden = false; + chartOnly = false; + } + + /** + * Sets the hidden flag + */ + void setHidden() + { + hidden = true; + } + + /** + * Sets the chart only flag + */ + void setChartOnly() + { + chartOnly = true; + } + + /** + * Gets the data to write out to the binary file + * + * @return the data to write out + */ + public byte[] getData() + { + data = new byte[name.length() * 2 + 8]; + + if (chartOnly) + { + data[5] = 0x02; + } + else + { + data[5] = 0; // set stream type to worksheet + } + + if (hidden) + { + data[4] = 0x1; + data[5] = 0x0; + } + + data[6] = (byte) name.length(); + data[7] = 1; + StringHelper.getUnicodeBytes(name, data, 8); + + return data; + } +} + + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/ButtonPropertySetRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/ButtonPropertySetRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/ButtonPropertySetRecord.java 17 Aug 2012 14:51:08 -0000 1.1 @@ -0,0 +1,72 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Any arbitrary excel record. Used during development only + */ +class ButtonPropertySetRecord extends WritableRecordData +{ + /** + * The binary data + */ + private byte[] data; + + /** + * Constructor + */ + public ButtonPropertySetRecord(jxl.read.biff.ButtonPropertySetRecord bps) + { + super(Type.BUTTONPROPERTYSET); + + data = bps.getData(); + } + + /** + * Constructor + */ + public ButtonPropertySetRecord(ButtonPropertySetRecord bps) + { + super(Type.BUTTONPROPERTYSET); + + data = bps.getData(); + } + + /** + * Retrieves the data to be written to the binary file + * + * @return the binary data + */ + public byte[] getData() + { + return data; + } +} + + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/CalcCountRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/CalcCountRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/CalcCountRecord.java 17 Aug 2012 14:51:11 -0000 1.1 @@ -0,0 +1,68 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which stores the maximum iterations option from the Options + * dialog box + */ +class CalcCountRecord extends WritableRecordData +{ + /** + * The iteration count + */ + private int calcCount; + /** + * The binary data to write to the output file + */ + private byte[] data; + + /** + * Constructor + * + * @param cnt the count indicator + */ + public CalcCountRecord(int cnt) + { + super(Type.CALCCOUNT); + calcCount = cnt; + } + + + /** + * Gets the data to write out to the file + * + * @return the binary data + */ + public byte[] getData() + { + byte[] data = new byte[2]; + + IntegerHelper.getTwoBytes(calcCount, data, 0); + + return data; + } +} + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/CalcModeRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/CalcModeRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/CalcModeRecord.java 17 Aug 2012 14:51:11 -0000 1.1 @@ -0,0 +1,95 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * The calculation mode for the workbook, as set from the Options + * dialog box + */ +class CalcModeRecord extends WritableRecordData +{ + /** + * The calculation mode (manual, automatic) + */ + private CalcMode calculationMode; + + private static class CalcMode + { + /** + * The indicator as written to the output file + */ + int value; + + /** + * Constructor + * + * @param m + */ + public CalcMode(int m) + { + value = m; + } + } + + /** + * Manual calculation + */ + static CalcMode manual = new CalcMode(0); + /** + * Automatic calculation + */ + static CalcMode automatic = new CalcMode(1); + /** + * Automatic calculation, except tables + */ + static CalcMode automaticNoTables = new CalcMode(-1); + + /** + * Constructor + * + * @param cm the calculation mode + */ + public CalcModeRecord(CalcMode cm) + { + super(Type.CALCMODE); + calculationMode = cm; + } + + + /** + * Gets the binary to data to write to the output file + * + * @return the binary data + */ + public byte[] getData() + { + byte[] data = new byte[2]; + + IntegerHelper.getTwoBytes(calculationMode.value, data, 0); + + return data; + } +} + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/CellValue.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/CellValue.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/CellValue.java 17 Aug 2012 14:51:08 -0000 1.1 @@ -0,0 +1,640 @@ +/********************************************************************* +* +* Copyright (C) 2001 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import common.Assert; +import common.Logger; + +import jxl.Cell; +import jxl.CellFeatures; +import jxl.Sheet; +import jxl.biff.DataValidation; +import jxl.biff.DataValiditySettingsRecord; +import jxl.biff.FormattingRecords; +import jxl.biff.IntegerHelper; +import jxl.biff.NumFormatRecordsException; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; +import jxl.biff.XFRecord; +import jxl.biff.drawing.ComboBox; +import jxl.biff.drawing.Comment; +import jxl.format.CellFormat; +import jxl.write.WritableCell; +import jxl.write.WritableCellFeatures; +import jxl.write.WritableWorkbook; + +/** + * Abstract class which stores the common data used for cells, such + * as row, column and formatting information. + * Any record which directly represents the contents of a cell, such + * as labels and numbers, are derived from this class + * data store + */ +public abstract class CellValue extends WritableRecordData + implements WritableCell +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(CellValue.class); + + /** + * The row in the worksheet at which this cell is located + */ + private int row; + + /** + * The column in the worksheet at which this cell is located + */ + private int column; + + /** + * The format applied to this cell + */ + private XFRecord format; + + /** + * A handle to the formatting records, used in case we want + * to change the format of the cell once it has been added + * to the spreadsheet + */ + private FormattingRecords formattingRecords; + + /** + * A flag to indicate that this record is already referenced within + * a worksheet + */ + private boolean referenced; + + /** + * A handle to the sheet + */ + private WritableSheetImpl sheet; + + /** + * The cell features + */ + private WritableCellFeatures features; + + /** + * Internal copied flag, to prevent cell features being added multiple + * times to the drawing array + */ + private boolean copied; + + /** + * Constructor used when building writable cells from the Java API + * + * @param c the column + * @param t the type indicator + * @param r the row + */ + protected CellValue(Type t, int c, int r) + { + this(t, c, r, WritableWorkbook.NORMAL_STYLE); + copied = false; + } + + /** + * Constructor used when creating a writable cell from a read-only cell + * (when copying a workbook) + * + * @param c the cell to clone + * @param t the type of this cell + */ + protected CellValue(Type t, Cell c) + { + this(t, c.getColumn(), c.getRow()); + copied = true; + + format = (XFRecord) c.getCellFormat(); + + if (c.getCellFeatures() != null) + { + features = new WritableCellFeatures(c.getCellFeatures()); + features.setWritableCell(this); + } + } + + /** + * Overloaded constructor used when building writable cells from the + * Java API which also takes a format + * + * @param c the column + * @param t the cell type + * @param r the row + * @param st the format to apply to this cell + */ + protected CellValue(Type t, int c, int r, CellFormat st) + { + super(t); + row = r; + column = c; + format = (XFRecord) st; + referenced = false; + copied = false; + } + + /** + * Copy constructor + * + * @param c the column + * @param t the cell type + * @param r the row + * @param cv the value to copy + */ + protected CellValue(Type t, int c, int r, CellValue cv) + { + super(t); + row = r; + column = c; + format = cv.format; + referenced = false; + copied = false; // used during a deep copy, so the cell features need + // to be added again + + if (cv.features != null) + { + features = new WritableCellFeatures(cv.features); + features.setWritableCell(this); + } + } + + /** + * An API function which sets the format to apply to this cell + * + * @param cf the format to apply to this cell + */ + public void setCellFormat(CellFormat cf) + { + format = (XFRecord) cf; + + // If the referenced flag has not been set, this cell has not + // been added to the spreadsheet, so we don't need to perform + // any further logic + if (!referenced) + { + return; + } + + // The cell has already been added to the spreadsheet, so the + // formattingRecords reference must be initialized + Assert.verify(formattingRecords != null); + + addCellFormat(); + } + + /** + * Returns the row number of this cell + * + * @return the row number of this cell + */ + public int getRow() + { + return row; + } + + /** + * Returns the column number of this cell + * + * @return the column number of this cell + */ + public int getColumn() + { + return column; + } + + /** + * Indicates whether or not this cell is hidden, by virtue of either + * the entire row or column being collapsed + * + * @return TRUE if this cell is hidden, FALSE otherwise + */ + public boolean isHidden() + { + ColumnInfoRecord cir = sheet.getColumnInfo(column); + + if (cir != null && cir.getWidth() == 0) + { + return true; + } + + RowRecord rr = sheet.getRowInfo(row); + + if (rr != null && (rr.getRowHeight() == 0 || rr.isCollapsed())) + { + return true; + } + + return false; + } + + /** + * Gets the data to write to the output file + * + * @return the binary data + */ + public byte[] getData() + { + byte[] mydata = new byte[6]; + IntegerHelper.getTwoBytes(row, mydata, 0); + IntegerHelper.getTwoBytes(column, mydata, 2); + IntegerHelper.getTwoBytes(format.getXFIndex(), mydata, 4); + return mydata; + } + + /** + * Called when the cell is added to the worksheet in order to indicate + * that this object is already added to the worksheet + * This method also verifies that the associated formats and formats + * have been initialized correctly + * + * @param fr the formatting records + * @param ss the shared strings used within the workbook + * @param s the sheet this is being added to + */ + void setCellDetails(FormattingRecords fr, SharedStrings ss, + WritableSheetImpl s) + { + referenced = true; + sheet = s; + formattingRecords = fr; + + addCellFormat(); + addCellFeatures(); + } + + /** + * Internal method to see if this cell is referenced within the workbook. + * Once this has been placed in the workbook, it becomes immutable + * + * @return TRUE if this cell has been added to a sheet, FALSE otherwise + */ + final boolean isReferenced() + { + return referenced; + } + + /** + * Gets the internal index of the formatting record + * + * @return the index of the format record + */ + final int getXFIndex() + { + return format.getXFIndex(); + } + + /** + * API method which gets the format applied to this cell + * + * @return the format for this cell + */ + public CellFormat getCellFormat() + { + return format; + } + + /** + * Increments the row of this cell by one. Invoked by the sheet when + * inserting rows + */ + void incrementRow() + { + row++; + + if (features != null) + { + Comment c = features.getCommentDrawing(); + if (c != null) + { + c.setX(column); + c.setY(row); + } + } + } + + /** + * Decrements the row of this cell by one. Invoked by the sheet when + * removing rows + */ + void decrementRow() + { + row--; + + if (features != null) + { + Comment c = features.getCommentDrawing(); + if ( c!= null) + { + c.setX(column); + c.setY(row); + } + + if (features.hasDropDown()) + { + logger.warn("need to change value for drop down drawing"); + } + } + } + + /** + * Increments the column of this cell by one. Invoked by the sheet when + * inserting columns + */ + void incrementColumn() + { + column++; + + if (features != null) + { + Comment c = features.getCommentDrawing(); + if (c != null) + { + c.setX(column); + c.setY(row); + } + } + + } + + /** + * Decrements the column of this cell by one. Invoked by the sheet when + * removing columns + */ + void decrementColumn() + { + column--; + + if (features != null) + { + Comment c = features.getCommentDrawing(); + if (c != null) + { + c.setX(column); + c.setY(row); + } + } + + } + + /** + * Called when a column is inserted on the specified sheet. Notifies all + * RCIR cells of this change. The default implementation here does nothing + * + * @param s the sheet on which the column was inserted + * @param sheetIndex the sheet index on which the column was inserted + * @param col the column number which was inserted + */ + void columnInserted(Sheet s, int sheetIndex, int col) + { + } + + /** + * Called when a column is removed on the specified sheet. Notifies all + * RCIR cells of this change. The default implementation here does nothing + * + * @param s the sheet on which the column was inserted + * @param sheetIndex the sheet index on which the column was inserted + * @param col the column number which was inserted + */ + void columnRemoved(Sheet s, int sheetIndex, int col) + { + } + + /** + * Called when a row is inserted on the specified sheet. Notifies all + * RCIR cells of this change. The default implementation here does nothing + * + * @param s the sheet on which the column was inserted + * @param sheetIndex the sheet index on which the column was inserted + * @param row the column number which was inserted + */ + void rowInserted(Sheet s, int sheetIndex, int row) + { + } + + /** + * Called when a row is inserted on the specified sheet. Notifies all + * RCIR cells of this change. The default implementation here does nothing + * + * @param s the sheet on which the row was removed + * @param sheetIndex the sheet index on which the column was removed + * @param row the column number which was removed + */ + void rowRemoved(Sheet s, int sheetIndex, int row) + { + } + + /** + * Accessor for the sheet containing this cell + * + * @return the sheet containing this cell + */ + protected WritableSheetImpl getSheet() + { + return sheet; + } + + /** + * Adds the format information to the shared records. Performs the necessary + * checks (and clones) to ensure that the formats are not shared. + * Called from setCellDetails and setCellFormat + */ + private void addCellFormat() + { + // Check to see if the format is one of the shared Workbook defaults. If + // so, then get hold of the Workbook's specific instance + Styles styles = sheet.getWorkbook().getStyles(); + format = styles.getFormat(format); + + try + { + if (!format.isInitialized()) + { + formattingRecords.addStyle(format); + } + } + catch (NumFormatRecordsException e) + { + logger.warn("Maximum number of format records exceeded. Using " + + "default format."); + format = styles.getNormalStyle(); + } + } + + /** + * Accessor for the cell features + * + * @return the cell features or NULL if this cell doesn't have any + */ + public CellFeatures getCellFeatures() + { + return features; + } + + /** + * Accessor for the cell features + * + * @return the cell features or NULL if this cell doesn't have any + */ + public WritableCellFeatures getWritableCellFeatures() + { + return features; + } + + /** + * Sets the cell features + * + * @param cf the cell features + */ + public void setCellFeatures(WritableCellFeatures cf) + { + if (features != null) + { + logger.warn("current cell features not null - overwriting"); + } + + features = cf; + cf.setWritableCell(this); + + // If the cell is already on the worksheet, then add the cell features + // to the workbook + if (referenced) + { + addCellFeatures(); + } + } + + /** + * Handles any addition cell features, such as comments or data + * validation. Called internally from this class when a cell is + * added to the workbook, and also externally from BaseCellFeatures + * following a call to setComment + */ + public final void addCellFeatures() + { + if (features == null) + { + return; + } + + if (copied == true) + { + copied = false; + + return; + } + + if (features.getComment() != null) + { + Comment comment = new Comment(features.getComment(), + column, row); + comment.setWidth(features.getCommentWidth()); + comment.setHeight(features.getCommentHeight()); + sheet.addDrawing(comment); + sheet.getWorkbook().addDrawing(comment); + features.setCommentDrawing(comment); + } + + if (features.hasDataValidation()) + { + try + { + features.getDVParser().setCell(column, + row, + sheet.getWorkbook(), + sheet.getWorkbook(), + sheet.getWorkbookSettings()); + } + catch (jxl.biff.formula.FormulaException e) + { + e.printStackTrace(); + Assert.verify(false); + } + sheet.addValidationCell(this); + if (!features.hasDropDown()) + { + return; + } + + // Get the combo box drawing object for list validations + if (sheet.getComboBox() == null) + { + ComboBox cb = new ComboBox(); + sheet.addDrawing(cb); + sheet.getWorkbook().addDrawing(cb); + sheet.setComboBox(cb); + } + + features.setComboBox(sheet.getComboBox()); + } + } + + /** + * Removes the cell features from the Workbook/Worksheet. Called when + * a cell is being removed/replaced from a worksheet + */ + public final void removeCellFeatures() + { + if (features == null) + { + return; + } + + // Remove the comment + features.removeComment(); + + // Remove the data validation + features.removeDataValidation(); + } + + + /** + * Called by the cell features to remove a comment + * + * @param c the comment to remove + */ + public final void removeComment(Comment c) + { + sheet.removeDrawing(c); + } + + /** + * Called by the cell features to remove the data validation + */ + public final void removeDataValidation() + { + sheet.removeDataValidation(this); + } + + /** + * Called when doing a copy of a writable object to indicate the source + * was writable than a read only copy and certain things (most notably + * the comments will need to be re-evaluated) + * + * @param boolean the copied flag + */ + final void setCopied(boolean c) + { + copied = c; + } + +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/CellXFRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/CellXFRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/CellXFRecord.java 17 Aug 2012 14:51:09 -0000 1.1 @@ -0,0 +1,238 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.DisplayFormat; +import jxl.biff.FontRecord; +import jxl.biff.XFRecord; +import jxl.format.Alignment; +import jxl.format.Border; +import jxl.format.BorderLineStyle; +import jxl.format.CellFormat; +import jxl.format.Colour; +import jxl.format.Orientation; +import jxl.format.Pattern; +import jxl.format.VerticalAlignment; +import jxl.write.WriteException; + +/** + * A cell XF Record + */ +public class CellXFRecord extends XFRecord +{ + /** + * Constructor + * + * @param fnt the font + * @param form the format + */ + protected CellXFRecord(FontRecord fnt, DisplayFormat form ) + { + super(fnt, form); + setXFDetails(XFRecord.cell,0); + } + + /** + * Copy constructor. Invoked when copying formats to handle cell merging + * + * @param fmt the format to copy + */ + CellXFRecord(XFRecord fmt ) + { + super(fmt); + setXFDetails(XFRecord.cell,0); + } + + /** + * A public copy constructor which can be used for copy formats between + * different sheets + */ + protected CellXFRecord(CellFormat format) + { + super(format); + } + + /** + * Sets the alignment for the cell + * + * @exception WriteException + * @param a the alignment + */ + public void setAlignment(Alignment a) throws WriteException + { + if (isInitialized()) + { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + super.setXFAlignment(a); + } + + /** + * Sets the background for the cell + * + * @exception WriteException + * @param c the background colour + * @param p the background patter + */ + public void setBackground(Colour c, Pattern p) throws WriteException + { + if (isInitialized()) + { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + super.setXFBackground(c, p); + super.setXFCellOptions(0x4000); + } + + /** + * Sets whether or not this XF record locks the cell + * + * @param l the locked flag + * @exception WriteException + */ + public void setLocked(boolean l) throws WriteException + { + if (isInitialized()) + { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + super.setXFLocked(l); + super.setXFCellOptions(0x8000); + } + + /** + * Sets the indentation of the cell text + * + * @param i the indentation + */ + public void setIndentation(int i) throws WriteException + { + if (isInitialized()) + { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + super.setXFIndentation(i); + } + + /** + * Sets the shrink to fit flag + * + * @param b the shrink to fit flag + */ + public void setShrinkToFit(boolean s) throws WriteException + { + if (isInitialized()) + { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + super.setXFShrinkToFit(s); + } + + /** + * Sets the vertical alignment for cells with this style + * + * @exception WriteException + * @param va the vertical alignment + */ + public void setVerticalAlignment(VerticalAlignment va) + throws WriteException + { + if (isInitialized()) + { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + + super.setXFVerticalAlignment(va); + } + + /** + * Sets the text orientation for cells with this style + * + * @exception WriteException + * @param o the orientation + */ + public void setOrientation(Orientation o) + throws WriteException + { + if (isInitialized()) + { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + + super.setXFOrientation(o); + } + + /** + * Sets the text wrapping for cells with this style. If the parameter is + * set to TRUE, then data in this cell will be wrapped around, and the + * cell's height adjusted accordingly + * + * @exception WriteException + * @param w the wrap + */ + public void setWrap(boolean w) throws WriteException + { + if (isInitialized()) + { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + + super.setXFWrap(w); + } + + /** + * Sets the border style for cells with this format + * + * @exception WriteException + * @param b the border + * @param ls the line for the specified border + */ + public void setBorder(Border b, BorderLineStyle ls, Colour c) + throws WriteException + { + if (isInitialized()) + { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + + if (b == Border.ALL) + { + // Apply to all + super.setXFBorder(Border.LEFT, ls, c); + super.setXFBorder(Border.RIGHT, ls, c); + super.setXFBorder(Border.TOP, ls, c); + super.setXFBorder(Border.BOTTOM, ls, c); + return; + } + + if (b == Border.NONE) + { + // Apply to all + super.setXFBorder(Border.LEFT, BorderLineStyle.NONE, Colour.BLACK); + super.setXFBorder(Border.RIGHT, BorderLineStyle.NONE, Colour.BLACK); + super.setXFBorder(Border.TOP, BorderLineStyle.NONE, Colour.BLACK); + super.setXFBorder(Border.BOTTOM, BorderLineStyle.NONE, Colour.BLACK); + return; + } + + super.setXFBorder(b, ls, c); + } +} + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/CodepageRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/CodepageRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/CodepageRecord.java 17 Aug 2012 14:51:10 -0000 1.1 @@ -0,0 +1,56 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Stores the default character set in operation when the workbook was + * saved + */ +class CodepageRecord extends WritableRecordData +{ + /** + * The binary data + */ + private byte[] data; + + /** + * Constructor + */ + public CodepageRecord() + { + super(Type.CODEPAGE); + + // Hard code inthe ANSI character set for Microsoft + data = new byte[] {(byte) 0xe4, (byte) 0x4}; + } + + /** + * Retrieves the data for output to binary file + * + * @return the data to be written + */ + public byte[] getData() + { + return data; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/ColumnInfoRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/ColumnInfoRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/ColumnInfoRecord.java 17 Aug 2012 14:51:09 -0000 1.1 @@ -0,0 +1,422 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + + +import jxl.biff.FormattingRecords; +import jxl.biff.IndexMapping; +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; +import jxl.biff.XFRecord; + +/** + * Describes the column formatting for a particular column + */ +class ColumnInfoRecord extends WritableRecordData +{ + /** + * The binary data + */ + private byte[] data; + /** + * The column number which this format applies to + */ + private int column; + /** + * The style for the column + */ + private XFRecord style; + /** + * The index for the style of this column + */ + private int xfIndex; + + /** + * The width of the column in 1/256 of a character + */ + private int width; + + /** + * Flag to indicate the hidden status of this column + */ + private boolean hidden; + + /** + * The column's outline level + */ + private int outlineLevel; + + /** + * Column collapsed flag + */ + private boolean collapsed; + + + /** + * Constructor used when setting column information from the user + * API + * + * @param w the width of the column in characters + * @param col the column to format + * @param xf the style for the column + */ + public ColumnInfoRecord(int col, int w, XFRecord xf) + { + super(Type.COLINFO); + + column = col; + width = w; + style = xf; + xfIndex = style.getXFIndex(); + hidden = false; + } + + /** + * Copy constructor used when copying from sheet to sheet within the + * same workbook + * + * @param the record to copy + */ + public ColumnInfoRecord(ColumnInfoRecord cir) + { + super(Type.COLINFO); + + column = cir.column; + width = cir.width; + style = cir.style; + xfIndex = cir.xfIndex; + hidden = cir.hidden; + outlineLevel = cir.outlineLevel; + collapsed = cir.collapsed; + + } + + + /** + * Constructor used when copying an existing spreadsheet + * + * @param col the column number + * @param cir the column info record read in + * @param fr the format records + */ + public ColumnInfoRecord(jxl.read.biff.ColumnInfoRecord cir, + int col, + FormattingRecords fr) + { + super(Type.COLINFO); + + column = col; + width = cir.getWidth(); + xfIndex = cir.getXFIndex(); + style = fr.getXFRecord(xfIndex); + outlineLevel = cir.getOutlineLevel(); + collapsed = cir.getCollapsed(); + } + + /** + * Constructor used when importing a sheet from another + * spreadsheet + * + * @param col the column number + * @param cir the column info record read in + */ + public ColumnInfoRecord(jxl.read.biff.ColumnInfoRecord cir, + int col) + { + super(Type.COLINFO); + + column = col; + width = cir.getWidth(); + xfIndex = cir.getXFIndex(); + outlineLevel = cir.getOutlineLevel(); + collapsed = cir.getCollapsed(); + } + + /** + * Gets the column this format applies to + * + * @return the column which is formatted + */ + public int getColumn() + { + return column; + } + + /** + * Increments the column. Called when inserting a new column into + * the sheet + */ + public void incrementColumn() + { + column++; + } + + /** + * Decrements the column. Called when removing a column from + * the sheet + */ + public void decrementColumn() + { + column--; + } + + /** + * Accessor for the width + * + * @return the width + */ + int getWidth() + { + return width; + } + + /** + * Sets the width. Used when autosizing columns + * + * @param w the new width + */ + void setWidth(int w) + { + width = w; + } + + /** + * Gets the binary data to be written to the output file + * + * @return the data to write to file + */ + public byte[] getData() + { + data = new byte[0x0c]; + + IntegerHelper.getTwoBytes(column, data, 0); + IntegerHelper.getTwoBytes(column, data, 2); + IntegerHelper.getTwoBytes(width, data, 4); + IntegerHelper.getTwoBytes(xfIndex, data, 6); + + // int options = 0x2; + int options = 0x6 | (outlineLevel << 8); + if (hidden) + { + options |= 0x1; + } + + outlineLevel = ((options & 0x700) / 0x100); + + if (collapsed) + { + options |= 0x1000; + } + + IntegerHelper.getTwoBytes(options, data, 8); + // IntegerHelper.getTwoBytes(2, data, 10); + + return data; + } + + /** + * Gets the cell format associated with this column info record + * + * @return the cell format for this column + */ + public XFRecord getCellFormat() + { + return style; + } + + /** + * Sets the cell format. Used when importing spreadsheets + * + * @param xfr the xf record + */ + public void setCellFormat(XFRecord xfr) + { + style = xfr; + } + + /** + * Accessor for the xf index, used when importing from another spreadsheet + * + * @return the xf index + */ + public int getXfIndex() + { + return xfIndex; + } + + /** + * Rationalizes the sheets xf index mapping + * @param xfmapping the index mapping + */ + void rationalize(IndexMapping xfmapping) + { + xfIndex = xfmapping.getNewIndex(xfIndex); + } + + /** + * Sets this column to be hidden (or otherwise) + * + * @param h TRUE if the column is to be hidden, FALSE otherwise + */ + void setHidden(boolean h) + { + hidden = h; + } + + /** + * Accessor for the hidden flag + * + * @return TRUE if this column is hidden, FALSE otherwise + */ + boolean getHidden() + { + return hidden; + } + + /** + * Standard equals method + * + * @return TRUE if these objects are equal, FALSE otherwise + */ + public boolean equals(Object o) + { + if (o == this) + { + return true; + } + + if (!(o instanceof ColumnInfoRecord)) + { + return false; + } + + ColumnInfoRecord cir = (ColumnInfoRecord) o; + + if (column != cir.column || + xfIndex != cir.xfIndex || + width != cir.width || + hidden != cir.hidden || + outlineLevel != cir.outlineLevel || + collapsed != cir.collapsed) + { + return false; + } + + if ((style == null && cir.style != null) || + (style != null && cir.style == null)) + { + return false; + } + + return style.equals(cir.style); + } + + /** + * Standard hashCode method + * + * @return the hashCode + */ + public int hashCode() + { + int hashValue = 137; + int oddPrimeNumber = 79; + + hashValue = hashValue * oddPrimeNumber + column; + hashValue = hashValue * oddPrimeNumber + xfIndex; + hashValue = hashValue * oddPrimeNumber + width; + hashValue = hashValue * oddPrimeNumber + (hidden ? 1:0); + + if (style != null) + { + hashValue ^= style.hashCode(); + } + + return hashValue; + } + + /** + * Accessor for the column's outline level + * + * @return the column's outline level + */ + public int getOutlineLevel() + { + return outlineLevel; + } + + /** + * Accessor for whether the column is collapsed + * + * @return the column's collapsed state + */ + public boolean getCollapsed() + { + return collapsed; + } + + /** + * Increments the column's outline level. This is how groups are made + * as well + */ + public void incrementOutlineLevel() + { + outlineLevel++; + } + + /** + * Decrements the column's outline level. This removes it from a + * grouping level. If + * all outline levels are gone the uncollapse the column. + */ + public void decrementOutlineLevel() + { + if (0 < outlineLevel) + { + outlineLevel--; + } + + if (0==outlineLevel) + { + collapsed = false; + } + } + + /** + * Sets the column's outline level + * + * @param level the column's outline level + */ + public void setOutlineLevel(int level) + { + outlineLevel = level; + } + + /** + * Sets the column's collapsed state + * + * @param value the column's collapsed state + */ + public void setCollapsed(boolean value) + { + collapsed = value; + } + +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/ColumnsExceededException.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/ColumnsExceededException.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/ColumnsExceededException.java 17 Aug 2012 14:51:11 -0000 1.1 @@ -0,0 +1,35 @@ +/********************************************************************* +* +* Copyright (C) 2005 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +/** + * Exception thrown when attempting to add a column to a spreadsheet which + * has already reached the maximum amount + */ +public class ColumnsExceededException extends JxlWriteException +{ + /** + * Constructor + */ + public ColumnsExceededException() + { + super(maxColumnsExceeded); + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/CompoundFile.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/CompoundFile.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/CompoundFile.java 17 Aug 2012 14:51:09 -0000 1.1 @@ -0,0 +1,1160 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; + +import common.Assert; +import common.Logger; + +import jxl.biff.BaseCompoundFile; +import jxl.biff.IntegerHelper; +import jxl.read.biff.BiffException; + +/** + * Writes out a compound file + * + * Header block is -1 + * Excel data is e..n (where e is the head extension blocks, normally 0 and + * n is at least 8) + * Summary information (8 blocks) + * Document summary (8 blocks) + * BBD is block p..q (where p=e+n+16 and q-p+1 is the number of BBD blocks) + * Property storage block is q+b...r (normally 1 block) (where b is the number + * of BBD blocks) + */ +final class CompoundFile extends BaseCompoundFile +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(CompoundFile.class); + + /** + * The stream to which the jumbled up data is written to + */ + private OutputStream out; + /** + * The organized biff records which form the actual excel data + */ + private ExcelDataOutput excelData; + + /** + * The size of the array + */ + private int size; + + /** + * The size the excel data should be in order to comply with the + * general compound file format + */ + private int requiredSize; + + /** + * The number of blocks it takes to store the big block depot + */ + private int numBigBlockDepotBlocks; + + /** + * The number of blocks it takes to store the small block depot chain + */ + private int numSmallBlockDepotChainBlocks; + + /** + * The number of blocks it takes to store the small block depot + */ + private int numSmallBlockDepotBlocks; + + /** + * The number of extension blocks required for the header to describe + * the BBD + */ + private int numExtensionBlocks; + + /** + * The extension block for the header + */ + private int extensionBlock; + + /** + * The number of blocks it takes to store the excel data + */ + private int excelDataBlocks; + + /** + * The start block of the root entry + */ + private int rootStartBlock; + + /** + * The start block of the excel data + */ + private int excelDataStartBlock; + + /** + * The start block of the big block depot + */ + private int bbdStartBlock; + + /** + * The start block of the small block depot + */ + private int sbdStartBlockChain; + + /** + * The start block of the small block depot + */ + private int sbdStartBlock; + + /** + * The number of big blocks required for additional property sets + */ + private int additionalPropertyBlocks; + + /** + * The number of small blocks + */ + private int numSmallBlocks; + + /** + * The total number of property sets in this compound file + */ + private int numPropertySets; + + /** + * The number of blocks required to store the root entry property sets + * and small block depot + */ + private int numRootEntryBlocks; + + /** + * The list of additional, non standard property sets names + */ + private ArrayList additionalPropertySets; + + /** + * The map of standard property sets, keyed on name + */ + private HashMap standardPropertySets; + + /** + * Structure used to store the property set and the data + */ + private static final class ReadPropertyStorage + { + PropertyStorage propertyStorage; + byte[] data; + int number; + + ReadPropertyStorage(PropertyStorage ps, byte[] d, int n) + { + propertyStorage = ps; + data = d; + number = n; + } + } + + + // The following member variables are used across methods when + // writing out the big block depot + /** + * The current position within the bbd. Used when writing out the + * BBD + */ + private int bbdPos; + + /** + * The current bbd block + */ + private byte[] bigBlockDepot; + + + /** + * Constructor + * + * @param l the length of the data + * @param os the output stream to write to + * @param data the excel data + * @param rcf the read compound + */ + public CompoundFile(ExcelDataOutput data, int l, OutputStream os, + jxl.read.biff.CompoundFile rcf) + throws CopyAdditionalPropertySetsException, IOException + { + super(); + size = l; + excelData = data; + + readAdditionalPropertySets(rcf); + + numRootEntryBlocks = 1; + numPropertySets = 4 + + (additionalPropertySets != null ? additionalPropertySets.size() : 0); + + + if (additionalPropertySets != null) + { + numSmallBlockDepotChainBlocks = getBigBlocksRequired(numSmallBlocks * 4); + numSmallBlockDepotBlocks = getBigBlocksRequired + (numSmallBlocks * SMALL_BLOCK_SIZE); + + numRootEntryBlocks += getBigBlocksRequired + (additionalPropertySets.size() * PROPERTY_STORAGE_BLOCK_SIZE); + } + + + int blocks = getBigBlocksRequired(l); + + // First pad the data out so that it fits nicely into a whole number + // of blocks + if (l < SMALL_BLOCK_THRESHOLD) + { + requiredSize = SMALL_BLOCK_THRESHOLD; + } + else + { + requiredSize = blocks * BIG_BLOCK_SIZE; + } + + out = os; + + + // Do the calculations + excelDataBlocks = requiredSize/BIG_BLOCK_SIZE; + numBigBlockDepotBlocks = 1; + + int blockChainLength = (BIG_BLOCK_SIZE - BIG_BLOCK_DEPOT_BLOCKS_POS)/4; + + int startTotalBlocks = excelDataBlocks + + 8 + // summary block + 8 + // document information + additionalPropertyBlocks + + numSmallBlockDepotBlocks + + numSmallBlockDepotChainBlocks + + numRootEntryBlocks; + + int totalBlocks = startTotalBlocks + numBigBlockDepotBlocks; + + // Calculate the number of BBD blocks needed to hold this info + numBigBlockDepotBlocks = (int) Math.ceil( (double) totalBlocks / + (double) (BIG_BLOCK_SIZE/4)); + + // Does this affect the total? + totalBlocks = startTotalBlocks + numBigBlockDepotBlocks; + + // And recalculate + numBigBlockDepotBlocks = (int) Math.ceil( (double) totalBlocks / + (double) (BIG_BLOCK_SIZE/4)); + + // Does this affect the total? + totalBlocks = startTotalBlocks + numBigBlockDepotBlocks; + + // See if the excel bbd chain can fit into the header block. + // Remember to allow for the end of chain indicator + if (numBigBlockDepotBlocks > blockChainLength - 1 ) + { + // Sod it - we need an extension block. We have to go through + // the whole tiresome calculation again + extensionBlock = 0; + + // Compute the number of extension blocks + int bbdBlocksLeft = numBigBlockDepotBlocks - blockChainLength + 1; + + numExtensionBlocks = (int) Math.ceil((double) bbdBlocksLeft / + (double) (BIG_BLOCK_SIZE/4 - 1)); + + // Modify the total number of blocks required and recalculate the + // the number of bbd blocks + totalBlocks = startTotalBlocks + + numExtensionBlocks + + numBigBlockDepotBlocks; + numBigBlockDepotBlocks = (int) Math.ceil( (double) totalBlocks / + (double) (BIG_BLOCK_SIZE/4)); + + // The final total + totalBlocks = startTotalBlocks + + numExtensionBlocks + + numBigBlockDepotBlocks; + } + else + { + extensionBlock = -2; + numExtensionBlocks = 0; + } + + // Set the excel data start block to be after the header (and + // its extensions) + excelDataStartBlock = numExtensionBlocks; + + // Set the start block of the small block depot + sbdStartBlock = -2; + if (additionalPropertySets != null && numSmallBlockDepotBlocks != 0) + { + sbdStartBlock = excelDataStartBlock + + excelDataBlocks + + additionalPropertyBlocks + + 16; + } + + // Set the sbd chain start block to be after the excel data and the + // small block depot + sbdStartBlockChain = -2; + + if (sbdStartBlock != -2) + { + sbdStartBlockChain = sbdStartBlock + numSmallBlockDepotBlocks; + } + + // Set the bbd start block to be after all the excel data + if (sbdStartBlockChain != -2) + { + bbdStartBlock = sbdStartBlockChain + + numSmallBlockDepotChainBlocks; + } + else + { + bbdStartBlock = excelDataStartBlock + + excelDataBlocks + + additionalPropertyBlocks + + 16; + } + + // Set the root start block to be after all the big block depot blocks + rootStartBlock = bbdStartBlock + + numBigBlockDepotBlocks; + + + if (totalBlocks != rootStartBlock + numRootEntryBlocks) + { + logger.warn("Root start block and total blocks are inconsistent " + + " generated file may be corrupt"); + logger.warn("RootStartBlock " + rootStartBlock + " totalBlocks " + totalBlocks); + } + } + + /** + * Reads the additional property sets from the read in compound file + * + * @param readCompoundFile the file read in + * @exception CopyAdditionalPropertySetsException + * @exception IOException + */ + private void readAdditionalPropertySets + (jxl.read.biff.CompoundFile readCompoundFile) + throws CopyAdditionalPropertySetsException, IOException + { + if (readCompoundFile == null) + { + return; + } + + additionalPropertySets = new ArrayList(); + standardPropertySets = new HashMap(); + int blocksRequired = 0; + + int numPropertySets = readCompoundFile.getNumberOfPropertySets(); + + for (int i = 0 ; i < numPropertySets ; i++) + { + PropertyStorage ps = readCompoundFile.getPropertySet(i); + + boolean standard = false; + + if (ps.name.equalsIgnoreCase(ROOT_ENTRY_NAME)) + { + standard = true; + ReadPropertyStorage rps = new ReadPropertyStorage(ps, null, i); + standardPropertySets.put(ROOT_ENTRY_NAME, rps); + } + + // See if it is a standard property set + for (int j = 0 ; j < STANDARD_PROPERTY_SETS.length && !standard ; j++) + { + if (ps.name.equalsIgnoreCase(STANDARD_PROPERTY_SETS[j])) + { + // See if it comes directly off the root entry + PropertyStorage ps2 = readCompoundFile.findPropertyStorage(ps.name); + Assert.verify(ps2 != null); + + if (ps2 == ps) + { + standard = true; + ReadPropertyStorage rps = new ReadPropertyStorage(ps, null, i); + standardPropertySets.put(STANDARD_PROPERTY_SETS[j], rps); + } + } + } + + if (!standard) + { + try + { + byte[] data = null; + if (ps.size > 0 ) + { + data = readCompoundFile.getStream(i); + } + else + { + data = new byte[0]; + } + ReadPropertyStorage rps = new ReadPropertyStorage(ps, data, i); + additionalPropertySets.add(rps); + + if (data.length > SMALL_BLOCK_THRESHOLD) + { + int blocks = getBigBlocksRequired(data.length); + blocksRequired += blocks; + } + else + { + int blocks = getSmallBlocksRequired(data.length); + numSmallBlocks += blocks; + } + } + catch (BiffException e) + { + logger.error(e); + throw new CopyAdditionalPropertySetsException(); + } + } + } + + additionalPropertyBlocks = blocksRequired; + } + + /** + * Writes out the excel file in OLE compound file format + * + * @exception IOException + */ + public void write() throws IOException + { + writeHeader(); + writeExcelData(); + writeDocumentSummaryData(); + writeSummaryData(); + writeAdditionalPropertySets(); + writeSmallBlockDepot(); + writeSmallBlockDepotChain(); + writeBigBlockDepot(); + writePropertySets(); + + // Don't flush or close the stream - this is handled by the enclosing File + // object + } + + /** + * Writes out any additional property sets + */ + private void writeAdditionalPropertySets() throws IOException + { + if (additionalPropertySets == null) + { + return; + } + + for (Iterator i = additionalPropertySets.iterator(); i.hasNext() ;) + { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + byte[] data = rps.data; + + if (data.length > SMALL_BLOCK_THRESHOLD) + { + int numBlocks = getBigBlocksRequired(data.length); + int requiredSize = numBlocks * BIG_BLOCK_SIZE; + + out.write(data, 0, data.length); + + byte[] padding = new byte[requiredSize - data.length]; + out.write(padding, 0, padding.length); + } + } + } + + /** + * Writes out the excel data, padding it out with empty bytes as + * necessary + * Also write out empty + * + * @exception IOException + */ + private void writeExcelData() throws IOException + { + excelData.writeData(out); + + byte[] padding = new byte[requiredSize - size]; + out.write(padding); + } + + /** + * Write out the document summary data. This is just blank + * + * @exception IOException + */ + private void writeDocumentSummaryData() throws IOException + { + byte[] padding = new byte[SMALL_BLOCK_THRESHOLD]; + + // Write out the summary information + out.write(padding); + } + + /** + * Write out the summary data. This is just blank + * + * @exception IOException + */ + private void writeSummaryData() throws IOException + { + byte[] padding = new byte[SMALL_BLOCK_THRESHOLD]; + + // Write out the summary information + out.write(padding); + } + + /** + * Writes the compound file header + * + * @exception IOException + */ + private void writeHeader() throws IOException + { + // Build up the header array + byte[] headerBlock = new byte[BIG_BLOCK_SIZE]; + byte[] extensionBlockData = new byte[BIG_BLOCK_SIZE * numExtensionBlocks]; + + // Copy in the identifier + System.arraycopy(IDENTIFIER, 0, headerBlock, 0, IDENTIFIER.length); + + // Copy in some magic values - no idea what they mean + headerBlock[0x18] = 0x3e; + headerBlock[0x1a] = 0x3; + headerBlock[0x1c] = (byte) 0xfe; + headerBlock[0x1d] = (byte) 0xff; + headerBlock[0x1e] = 0x9; + headerBlock[0x20] = 0x6; + headerBlock[0x39] = 0x10; + + // Set the number of BBD blocks + IntegerHelper.getFourBytes(numBigBlockDepotBlocks, + headerBlock, + NUM_BIG_BLOCK_DEPOT_BLOCKS_POS); + + // Set the small block depot chain + IntegerHelper.getFourBytes(sbdStartBlockChain, + headerBlock, + SMALL_BLOCK_DEPOT_BLOCK_POS); + + // Set the number of blocks in the small block depot chain + IntegerHelper.getFourBytes(numSmallBlockDepotChainBlocks, + headerBlock, + NUM_SMALL_BLOCK_DEPOT_BLOCKS_POS); + + // Set the extension block + IntegerHelper.getFourBytes(extensionBlock, + headerBlock, + EXTENSION_BLOCK_POS); + + // Set the number of extension blocks to be the number of BBD blocks - 1 + IntegerHelper.getFourBytes(numExtensionBlocks, + headerBlock, + NUM_EXTENSION_BLOCK_POS); + + // Set the root start block + IntegerHelper.getFourBytes(rootStartBlock, + headerBlock, + ROOT_START_BLOCK_POS); + + // Set the block numbers for the BBD. Set the BBD running + // after the excel data and summary information + int pos = BIG_BLOCK_DEPOT_BLOCKS_POS; + + // See how many blocks fit into the header + int blocksToWrite = Math.min(numBigBlockDepotBlocks, + (BIG_BLOCK_SIZE - + BIG_BLOCK_DEPOT_BLOCKS_POS)/4); + int blocksWritten = 0; + + for (int i = 0 ; i < blocksToWrite; i++) + { + IntegerHelper.getFourBytes(bbdStartBlock + i, + headerBlock, + pos); + pos += 4; + blocksWritten++; + } + + // Pad out the rest of the header with blanks + for (int i = pos; i < BIG_BLOCK_SIZE; i++) + { + headerBlock[i] = (byte) 0xff; + } + + out.write(headerBlock); + + // Write out the extension blocks + pos = 0; + + for (int extBlock = 0; extBlock < numExtensionBlocks; extBlock++) + { + blocksToWrite = Math.min(numBigBlockDepotBlocks - blocksWritten, + BIG_BLOCK_SIZE/4 -1); + + for(int j = 0 ; j < blocksToWrite; j++) + { + IntegerHelper.getFourBytes(bbdStartBlock + blocksWritten + j, + extensionBlockData, + pos); + pos += 4; + } + + blocksWritten += blocksToWrite; + + // Indicate the next block, or the termination of the chain + int nextBlock = (blocksWritten == numBigBlockDepotBlocks) ? + -2 : extBlock+1 ; + IntegerHelper.getFourBytes(nextBlock, extensionBlockData, pos); + pos +=4; + } + + if (numExtensionBlocks > 0) + { + // Pad out the rest of the extension block with blanks + for (int i = pos; i < extensionBlockData.length; i++) + { + extensionBlockData[i] = (byte) 0xff; + } + + out.write(extensionBlockData); + } + } + + /** + * Checks that the data can fit into the current BBD block. If not, + * then it moves on to the next block + * + * @exception IOException + */ + private void checkBbdPos() throws IOException + { + if (bbdPos >= BIG_BLOCK_SIZE) + { + // Write out the extension block. This will simply be the next block + out.write(bigBlockDepot); + + // Create a new block + bigBlockDepot = new byte[BIG_BLOCK_SIZE]; + bbdPos = 0; + } + } + + /** + * Writes out the big block chain + * + * @param startBlock the starting block of the big block chain + * @param numBlocks the number of blocks in the chain + * @exception IOException + */ + private void writeBlockChain(int startBlock, int numBlocks) + throws IOException + { + int blocksToWrite = numBlocks - 1; + int blockNumber = startBlock + 1; + + while (blocksToWrite > 0) + { + int bbdBlocks = Math.min(blocksToWrite, (BIG_BLOCK_SIZE - bbdPos)/4); + + for (int i = 0 ; i < bbdBlocks; i++) + { + IntegerHelper.getFourBytes(blockNumber, bigBlockDepot, bbdPos); + bbdPos +=4 ; + blockNumber++; + } + + blocksToWrite -= bbdBlocks; + checkBbdPos(); + } + + // Write the end of the block chain + IntegerHelper.getFourBytes(-2, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + } + + /** + * Writes the block chains for the additional property sets + * + * @exception IOException + */ + private void writeAdditionalPropertySetBlockChains() throws IOException + { + if (additionalPropertySets == null) + { + return; + } + + int blockNumber = excelDataStartBlock + excelDataBlocks + 16; + for (Iterator i = additionalPropertySets.iterator(); i.hasNext() ; ) + { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + if (rps.data.length > SMALL_BLOCK_THRESHOLD) + { + int numBlocks = getBigBlocksRequired(rps.data.length); + + writeBlockChain(blockNumber, numBlocks); + blockNumber += numBlocks; + } + } + } + + /** + * Writes out the chains for the small block depot + */ + private void writeSmallBlockDepotChain() throws IOException + { + if (sbdStartBlockChain == -2) + { + return; + } + + byte[] smallBlockDepotChain = + new byte[numSmallBlockDepotChainBlocks * BIG_BLOCK_SIZE]; + + int pos = 0; + int sbdBlockNumber = 1; + + for (Iterator i = additionalPropertySets.iterator(); i.hasNext() ; ) + { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + + if (rps.data.length <= SMALL_BLOCK_THRESHOLD && + rps.data.length != 0) + { + int numSmallBlocks = getSmallBlocksRequired(rps.data.length); + for (int j = 0 ; j < numSmallBlocks - 1 ; j++) + { + IntegerHelper.getFourBytes(sbdBlockNumber, + smallBlockDepotChain, + pos); + pos += 4; + sbdBlockNumber++; + } + + // Write out the end of chain + IntegerHelper.getFourBytes(-2, smallBlockDepotChain, pos); + pos += 4; + sbdBlockNumber++; + } + } + + out.write(smallBlockDepotChain); + } + + /** + * Writes out all the data in the small block depot + * + * @exception + */ + private void writeSmallBlockDepot() throws IOException + { + if (additionalPropertySets == null) + { + return; + } + + byte[] smallBlockDepot = + new byte[numSmallBlockDepotBlocks * BIG_BLOCK_SIZE]; + + int pos = 0; + + for (Iterator i = additionalPropertySets.iterator() ; i.hasNext() ; ) + { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + + if (rps.data.length <= SMALL_BLOCK_THRESHOLD) + { + int smallBlocks = getSmallBlocksRequired(rps.data.length); + int length = smallBlocks * SMALL_BLOCK_SIZE; + System.arraycopy(rps.data, 0, smallBlockDepot, pos, rps.data.length); + pos += length; + } + } + + out.write(smallBlockDepot); + } + + /** + * Writes out the Big Block Depot + * + * @exception IOException + */ + private void writeBigBlockDepot() throws IOException + { + // This is after the excel data, the summary information, the + // big block property sets and the small block depot + bigBlockDepot = new byte[BIG_BLOCK_SIZE]; + bbdPos = 0; + + // Write out the extension blocks, indicating them as special blocks + for (int i = 0 ; i < numExtensionBlocks; i++) + { + IntegerHelper.getFourBytes(-3, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + } + + writeBlockChain(excelDataStartBlock, excelDataBlocks); + + // The excel data has been written. Now write out the rest of it + + // Write the block chain for the summary information + int summaryInfoBlock = excelDataStartBlock + + excelDataBlocks + + additionalPropertyBlocks; + + for (int i = summaryInfoBlock; i < summaryInfoBlock + 7; i++) + { + IntegerHelper.getFourBytes(i + 1, bigBlockDepot, bbdPos); + bbdPos +=4 ; + checkBbdPos(); + } + + // Write the end of the block chain for the summary info block + IntegerHelper.getFourBytes(-2, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + + // Write the block chain for the document summary information + for (int i = summaryInfoBlock + 8; i < summaryInfoBlock + 15; i++) + { + IntegerHelper.getFourBytes(i + 1, bigBlockDepot, bbdPos); + bbdPos +=4 ; + checkBbdPos(); + } + + // Write the end of the block chain for the document summary + IntegerHelper.getFourBytes(-2, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + + // Write out the block chain for the copied property sets, if present + writeAdditionalPropertySetBlockChains(); + + if (sbdStartBlock != -2) + { + // Write out the block chain for the small block depot + writeBlockChain(sbdStartBlock, numSmallBlockDepotBlocks); + + // Write out the block chain for the small block depot chain + writeBlockChain(sbdStartBlockChain, numSmallBlockDepotChainBlocks); + } + + // The Big Block Depot immediately follows. Denote these as a special + // block + for (int i = 0; i < numBigBlockDepotBlocks; i++) + { + IntegerHelper.getFourBytes(-3, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + } + + // Write the root entry + writeBlockChain(rootStartBlock, numRootEntryBlocks); + + // Pad out the remainder of the block + if (bbdPos != 0) + { + for (int i = bbdPos; i < BIG_BLOCK_SIZE; i++) + { + bigBlockDepot[i] = (byte) 0xff; + } + out.write(bigBlockDepot); + } + } + + /** + * Calculates the number of big blocks required to store data of the + * specified length + * + * @param length the length of the data + * @return the number of big blocks required to store the data + */ + private int getBigBlocksRequired(int length) + { + int blocks = length / BIG_BLOCK_SIZE; + + return (length % BIG_BLOCK_SIZE > 0 )? blocks + 1 : blocks; + } + + /** + * Calculates the number of small blocks required to store data of the + * specified length + * + * @param length the length of the data + * @return the number of small blocks required to store the data + */ + private int getSmallBlocksRequired(int length) + { + int blocks = length / SMALL_BLOCK_SIZE; + + return (length % SMALL_BLOCK_SIZE > 0 )? blocks + 1 : blocks; + } + + /** + * Writes out the property sets + * + * @exception IOException + */ + private void writePropertySets() throws IOException + { + byte[] propertySetStorage = new byte[BIG_BLOCK_SIZE * numRootEntryBlocks]; + + int pos = 0; + int[] mappings = null; + + // Build up the mappings array + if (additionalPropertySets != null) + { + mappings = new int[numPropertySets]; + + // Map the standard ones to the first four + for (int i = 0 ; i < STANDARD_PROPERTY_SETS.length ; i++) + { + ReadPropertyStorage rps = (ReadPropertyStorage) + standardPropertySets.get(STANDARD_PROPERTY_SETS[i]); + + if (rps != null) + { + mappings[rps.number] = i; + } + else + { + logger.warn("Standard property set " + STANDARD_PROPERTY_SETS[i] + + " not present in source file"); + } + } + + // Now go through the original ones + int newMapping = STANDARD_PROPERTY_SETS.length; + for (Iterator i = additionalPropertySets.iterator(); i.hasNext(); ) + { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + mappings[rps.number] = newMapping; + newMapping++; + } + } + + int child = 0; + int previous = 0; + int next = 0; + + // Compute the size of the root property set + int size = 0; + + if (additionalPropertySets != null) + { + // Workbook + size += getBigBlocksRequired(requiredSize) * BIG_BLOCK_SIZE; + + // The two information blocks + size += getBigBlocksRequired(SMALL_BLOCK_THRESHOLD) * BIG_BLOCK_SIZE; + size += getBigBlocksRequired(SMALL_BLOCK_THRESHOLD) * BIG_BLOCK_SIZE; + + // Additional property sets + for (Iterator i = additionalPropertySets.iterator(); i.hasNext(); ) + { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + if (rps.propertyStorage.type != 1) + { + if (rps.propertyStorage.size >= SMALL_BLOCK_THRESHOLD) + { + size += getBigBlocksRequired(rps.propertyStorage.size) * + BIG_BLOCK_SIZE; + } + else + { + size += getSmallBlocksRequired(rps.propertyStorage.size) * + SMALL_BLOCK_SIZE; + } + } + } + } + + // Set the root entry property set + PropertyStorage ps = new PropertyStorage(ROOT_ENTRY_NAME); + ps.setType(5); + ps.setStartBlock(sbdStartBlock); + ps.setSize(size); + ps.setPrevious(-1); + ps.setNext(-1); + ps.setColour(0); + + child = 1; + if (additionalPropertySets != null) + { + ReadPropertyStorage rps = (ReadPropertyStorage) + standardPropertySets.get(ROOT_ENTRY_NAME); + child = mappings[rps.propertyStorage.child]; + } + ps.setChild(child); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + + // Set the workbook property set + ps = new PropertyStorage(WORKBOOK_NAME); + ps.setType(2); + ps.setStartBlock(excelDataStartBlock); + // start the excel data after immediately after this block + ps.setSize(requiredSize); + // always use a big block stream - none of that messing around + // with small blocks + + previous = 3; + next = -1; + + if (additionalPropertySets != null) + { + ReadPropertyStorage rps = (ReadPropertyStorage) + standardPropertySets.get(WORKBOOK_NAME); + previous = rps.propertyStorage.previous != -1 ? + mappings[rps.propertyStorage.previous] : -1; + next = rps.propertyStorage.next != -1 ? + mappings[rps.propertyStorage.next] : -1 ; + } + + ps.setPrevious(previous); + ps.setNext(next); + ps.setChild(-1); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + // Set the summary information + ps = new PropertyStorage(SUMMARY_INFORMATION_NAME); + ps.setType(2); + ps.setStartBlock(excelDataStartBlock + excelDataBlocks); + ps.setSize(SMALL_BLOCK_THRESHOLD); + + previous = 1; + next = 3; + + if (additionalPropertySets != null) + { + ReadPropertyStorage rps = (ReadPropertyStorage) + standardPropertySets.get(SUMMARY_INFORMATION_NAME); + + if (rps != null) + { + previous = rps.propertyStorage.previous != - 1 ? + mappings[rps.propertyStorage.previous] : -1 ; + next = rps.propertyStorage.next != - 1 ? + mappings[rps.propertyStorage.next] : -1 ; + } + } + + ps.setPrevious(previous); + ps.setNext(next); + ps.setChild(-1); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + // Set the document summary information + ps = new PropertyStorage(DOCUMENT_SUMMARY_INFORMATION_NAME); + ps.setType(2); + ps.setStartBlock(excelDataStartBlock + excelDataBlocks + 8); + ps.setSize(SMALL_BLOCK_THRESHOLD); + ps.setPrevious(-1); + ps.setNext(-1); + ps.setChild(-1); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + + + // Write out the additional property sets + if (additionalPropertySets == null) + { + out.write(propertySetStorage); + return; + } + + int bigBlock = excelDataStartBlock + excelDataBlocks + 16; + int smallBlock = 0; + + for (Iterator i = additionalPropertySets.iterator() ; i.hasNext(); ) + { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + + int block = rps.data.length > SMALL_BLOCK_THRESHOLD ? + bigBlock : smallBlock; + + ps = new PropertyStorage(rps.propertyStorage.name); + ps.setType(rps.propertyStorage.type); + ps.setStartBlock(block); + ps.setSize(rps.propertyStorage.size); + // ps.setColour(rps.propertyStorage.colour); + + previous = rps.propertyStorage.previous != -1 ? + mappings[rps.propertyStorage.previous] : -1; + next = rps.propertyStorage.next != -1 ? + mappings[rps.propertyStorage.next] : -1; + child = rps.propertyStorage.child != -1 ? + mappings[rps.propertyStorage.child] : -1; + + ps.setPrevious(previous); + ps.setNext(next); + ps.setChild(child); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + if (rps.data.length > SMALL_BLOCK_THRESHOLD) + { + bigBlock += getBigBlocksRequired(rps.data.length); + } + else + { + smallBlock += getSmallBlocksRequired(rps.data.length); + } + } + + out.write(propertySetStorage); + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/CompoundFile.java2 =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/CompoundFile.java2,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/CompoundFile.java2 17 Aug 2012 14:51:10 -0000 1.1 @@ -0,0 +1,1217 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import java.io.OutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.HashMap; + +import common.Assert; +import common.Logger; +import jxl.biff.BaseCompoundFile; +import jxl.biff.IntegerHelper; +import jxl.read.biff.BiffException; + +/** + * Writes out a compound file + * + * Header block is -1 + * Excel data is e..n (where is the head extension blocks, normally 0 and + * n is at least 8) + * Summary information (8 blocks) + * Document summary (8 blocks) + * BBD is block p..q (where p=e+n+16 and q-p+1 is the number of BBD blocks) + * Property storage block is q+b...r (normally 1 block) (where b is the number + * of BBD blocks) + */ +final class CompoundFile extends BaseCompoundFile +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(CompoundFile.class); + + /** + * The stream to which the jumbled up data is written to + */ + private OutputStream out; + /** + * The organized biff records which form the actual excel data + */ + private byte[] excelData; + + /** + * The size of the array + */ + private int size; + + /** + * The size the excel data should be in order to comply with the + * general compound file format + */ + private int requiredSize; + + /** + * The number of blocks it takes to store the big block depot + */ + private int numBigBlockDepotBlocks; + + /** + * The number of blocks it takes to store the small block depot chain + */ + private int numSmallBlockDepotChainBlocks; + + /** + * The number of blocks it takes to store the small block depot + */ + private int numSmallBlockDepotBlocks; + + /** + * The number of extension blocks required for the header to describe + * the BBD + */ + private int numExtensionBlocks; + + /** + * The extension block for the header + */ + private int extensionBlock; + + /** + * The number of blocks it takes to store the excel data + */ + private int excelDataBlocks; + + /** + * The start block of the root entry + */ + private int rootStartBlock; + + /** + * The start block of the excel data + */ + private int excelDataStartBlock; + + /** + * The start block of the big block depot + */ + private int bbdStartBlock; + + /** + * The start block of the small block depot + */ + private int sbdStartBlockChain; + + /** + * The start block of the small block depot + */ + private int sbdStartBlock; + + /** + * The number of big blocks required for additional property sets + */ + private int additionalPropertyBlocks; + + /** + * The number of small blocks + */ + private int numSmallBlocks; + + /** + * The total number of property sets in this compound file + */ + private int numPropertySets; + + /** + * The number of blocks required to store the root entry property sets + * and small block depot + */ + private int numRootEntryBlocks; + + /** + * The list of additional, non standard property sets names + */ + private ArrayList additionalPropertySets; + + /** + * A hash map of the original property sets keyed on name + */ + private HashMap readPropertySets; + + /** + * The array of standard property set mappings + */ + private int[] standardPropertySetMappings; + + private ReadPropertyStorage rootEntryPropertySet; + + /** + * Structure used to store the property set and the data + */ + private static final class ReadPropertyStorage + { + PropertyStorage propertyStorage; + byte[] data; + int number; + + ReadPropertyStorage(PropertyStorage ps, byte[] d, int n) + { + propertyStorage = ps; + data = d; + number = n; + } + } + + + // The following member variables are used across methods when + // writing out the big block depot + /** + * The current position within the bbd. Used when writing out the + * BBD + */ + private int bbdPos; + + /** + * The current bbd block + */ + private byte[] bigBlockDepot; + + + /** + * Constructor + * + * @param l the length of the data + * @param os the output stream to write to + * @param data the excel data + * @param rcf the read compound + */ + public CompoundFile(byte[] data, int l, OutputStream os, + jxl.read.biff.CompoundFile rcf) + throws CopyAdditionalPropertySetsException, IOException + { + super(); + size = l; + excelData = data; + + readAdditionalPropertySets(rcf); + + numRootEntryBlocks = 1; + numPropertySets = 4 + + (additionalPropertySets != null ? additionalPropertySets.size() : 0); + + logger.debug("numSmallBlocks " + numSmallBlocks); + + if (additionalPropertySets != null) + { + /* + try + { + rootEntryPropertySet = new ReadPropertyStorage(rcf.getPropertySet(ROOT_ENTRY_NAME), rcf.getStream(ROOT_ENTRY_NAME), 0); + numSmallBlocks += getSmallBlocksRequired(rootEntryPropertySet.data.length); + logger.debug("small blocks for root entry " + getSmallBlocksRequired(rootEntryPropertySet.data.length)); + } + catch (BiffException e) + { + e.printStackTrace(); + } + */ + + numSmallBlockDepotChainBlocks = getBigBlocksRequired(numSmallBlocks * 4); + numSmallBlockDepotBlocks = getBigBlocksRequired + (numSmallBlocks * SMALL_BLOCK_SIZE); + + numRootEntryBlocks += getBigBlocksRequired + (additionalPropertySets.size() * PROPERTY_STORAGE_BLOCK_SIZE); + } + + logger.debug("root entry requires " + numRootEntryBlocks + " blocks"); + + int blocks = getBigBlocksRequired(l); + + // First pad the data out so that it fits nicely into a whole number + // of blocks + if (l < SMALL_BLOCK_THRESHOLD) + { + requiredSize = SMALL_BLOCK_THRESHOLD; + } + else + { + requiredSize = blocks * BIG_BLOCK_SIZE; + } + + out = os; + + // logger.debug("smallBlockDepot requires " + numSmallBlockDepotBlocks + " big blocks"); + + // Do the calculations + excelDataBlocks = requiredSize/BIG_BLOCK_SIZE; + numBigBlockDepotBlocks = 1; + + int blockChainLength = (BIG_BLOCK_SIZE - BIG_BLOCK_DEPOT_BLOCKS_POS)/4; + + int startTotalBlocks = excelDataBlocks + + 8 + // summary block + 8 + // document information + additionalPropertyBlocks + + numSmallBlockDepotBlocks + + numSmallBlockDepotChainBlocks + + numRootEntryBlocks; + + int totalBlocks = startTotalBlocks + numBigBlockDepotBlocks; + + // Calculate the number of BBD blocks needed to hold this info + numBigBlockDepotBlocks = (int) Math.ceil( (double) totalBlocks / + (double) (BIG_BLOCK_SIZE/4)); + + // Does this affect the total? + totalBlocks = startTotalBlocks + numBigBlockDepotBlocks; + + // And recalculate + numBigBlockDepotBlocks = (int) Math.ceil( (double) totalBlocks / + (double) (BIG_BLOCK_SIZE/4)); + + // Does this affect the total? + totalBlocks = startTotalBlocks + numBigBlockDepotBlocks; + + // See if the excel bbd chain can fit into the header block. + // Remember to allow for the end of chain indicator + if (numBigBlockDepotBlocks > blockChainLength - 1 ) + { + // Sod it - we need an extension block. We have to go through + // the whole tiresome calculation again + extensionBlock = 0; + + // Compute the number of extension blocks + int bbdBlocksLeft = numBigBlockDepotBlocks - blockChainLength + 1; + + numExtensionBlocks = (int) Math.ceil((double) bbdBlocksLeft / + (double) (BIG_BLOCK_SIZE/4 - 1)); + + // Modify the total number of blocks required and recalculate the + // the number of bbd blocks + totalBlocks = startTotalBlocks + + numExtensionBlocks + + numBigBlockDepotBlocks; + numBigBlockDepotBlocks = (int) Math.ceil( (double) totalBlocks / + (double) (BIG_BLOCK_SIZE/4)); + + // The final total + totalBlocks = startTotalBlocks + + numExtensionBlocks + + numBigBlockDepotBlocks; + } + else + { + extensionBlock = -2; + numExtensionBlocks = 0; + } + + // Set the excel data start block to be after the header (and + // its extensions) + excelDataStartBlock = numExtensionBlocks; + + logger.debug("excelDataStartBlock " + excelDataStartBlock); + + // Set the start block of the small block depot + sbdStartBlock = -2; + if (additionalPropertySets != null) + { + sbdStartBlock = excelDataStartBlock + + excelDataBlocks + + additionalPropertyBlocks + + 16; + } + + // Set the sbd chain start block to be after the excel data and the + // small block depot + sbdStartBlockChain = -2; + + if (sbdStartBlock != -2) + { + sbdStartBlockChain = sbdStartBlock + numSmallBlockDepotBlocks; + } + + logger.debug("sbdStartBlockChain " + sbdStartBlockChain); + + // Set the bbd start block to be after all the excel data + if (sbdStartBlockChain != -2) + { + bbdStartBlock = sbdStartBlockChain + + numSmallBlockDepotChainBlocks; + } + else + { + bbdStartBlock = excelDataStartBlock + + excelDataBlocks + + additionalPropertyBlocks + + 16; + } + + logger.debug("bbdStartBlock " + bbdStartBlock); + + // Set the root start block to be after all the big block depot blocks + rootStartBlock = bbdStartBlock + + numBigBlockDepotBlocks; + + + if (totalBlocks != rootStartBlock + numRootEntryBlocks) + { + logger.warn("Root start block and total blocks are inconsistent " + + " generated file may be corrupt"); + logger.warn("RootStartBlock " + rootStartBlock + " totalBlocks " + totalBlocks); + } + } + + /** + * Reads the additional property sets from the read in compound file + * + * @return the number of blocks needed to store these property sets + */ + private void readAdditionalPropertySets + (jxl.read.biff.CompoundFile readCompoundFile) + throws CopyAdditionalPropertySetsException, IOException + { + if (readCompoundFile == null) + { + return; + } + + additionalPropertySets = new ArrayList(); + readPropertySets = new HashMap(); + + String[] psnames = readCompoundFile.getPropertySetNames(); + int blocksRequired = 0; + standardPropertySetMappings = new int[STANDARD_PROPERTY_SETS.length]; + + for (int i = 0 ; i < psnames.length ; i++) + { + // Add it to the hash map for later + PropertyStorage ps = readCompoundFile.getPropertySet(psnames[i]); + + // If the name is non standard, then retrieve the property set + // information + boolean standard = false; + for (int j = 0 ; j < STANDARD_PROPERTY_SETS.length && !standard ; j++) + { + if (psnames[i].equalsIgnoreCase(STANDARD_PROPERTY_SETS[j])) + { + standard = true; + ReadPropertyStorage rps = new ReadPropertyStorage(ps, null, i); + readPropertySets.put(psnames[i], rps); + } + } + + if (!standard) + { + try + { + byte[] data = null; + if (ps.size > 0 ) + { + data = readCompoundFile.getStream(ps.name); + } + else + { + data = new byte[0]; + } + ReadPropertyStorage rps = new ReadPropertyStorage(ps, data, i); + readPropertySets.put(psnames[i], rps); + additionalPropertySets.add(rps); + + if (data.length > SMALL_BLOCK_THRESHOLD) + { + int blocks = getBigBlocksRequired(data.length); + blocksRequired += blocks; + } + else + { + int blocks = getSmallBlocksRequired(data.length); + numSmallBlocks += blocks; + } + } + catch (BiffException e) + { + logger.error(e); + throw new CopyAdditionalPropertySetsException(); + } + } + } + + additionalPropertyBlocks = blocksRequired; + } + + + /** + * Writes out the excel file in OLE compound file format + * + * @exception IOException + */ + public void write() throws IOException + { + writeHeader(); + writeExcelData(); + writeDocumentSummaryData(); + writeSummaryData(); + writeAdditionalPropertySets(); + writeSmallBlockDepot(); + writeSmallBlockDepotChain(); + writeBigBlockDepot(); + writePropertySets(); + + // Don't flush or close the stream - this is handled by the enclosing File + // object + } + + /** + * Writes out any additional property sets + */ + private void writeAdditionalPropertySets() throws IOException + { + if (additionalPropertySets == null) + { + return; + } + + for (Iterator i = additionalPropertySets.iterator(); i.hasNext() ;) + { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + byte[] data = rps.data; + + if (data.length > SMALL_BLOCK_THRESHOLD) + { + logger.debug("Writing big block chain for property set " + rps.propertyStorage.name); + int numBlocks = getBigBlocksRequired(data.length); + int requiredSize = numBlocks * BIG_BLOCK_SIZE; + + out.write(data, 0, data.length); + + byte[] padding = new byte[requiredSize - data.length]; + out.write(padding, 0, padding.length); + } + } + } + + /** + * Writes out the excel data, padding it out with empty bytes as + * necessary + * Also write out empty + * + * @exception IOException + */ + private void writeExcelData() throws IOException + { + logger.debug("num excel data blocks " + excelDataBlocks + " excelData size " + requiredSize); + out.write(excelData, 0, size); + + byte[] padding = new byte[requiredSize - size]; + out.write(padding); + } + + /** + * Write out the document summary data. This is just blank + * + * @exception IOException + */ + private void writeDocumentSummaryData() throws IOException + { + byte[] padding = new byte[SMALL_BLOCK_THRESHOLD]; + + // Write out the summary information + out.write(padding); + } + + /** + * Write out the summary data. This is just blank + * + * @exception IOException + */ + private void writeSummaryData() throws IOException + { + byte[] padding = new byte[SMALL_BLOCK_THRESHOLD]; + + // Write out the summary information + out.write(padding); + } + + /** + * Writes the compound file header + * + * @exception IOException + */ + private void writeHeader() throws IOException + { + logger.debug("num extensions blocks for header: " + numExtensionBlocks); + // Build up the header array + byte[] headerBlock = new byte[BIG_BLOCK_SIZE]; + byte[] extensionBlockData = new byte[BIG_BLOCK_SIZE * numExtensionBlocks]; + + // Copy in the identifier + System.arraycopy(IDENTIFIER, 0, headerBlock, 0, IDENTIFIER.length); + + // Copy in some magic values - no idea what they mean + headerBlock[0x18] = 0x3e; + headerBlock[0x1a] = 0x3; + headerBlock[0x1c] = (byte) 0xfe; + headerBlock[0x1d] = (byte) 0xff; + headerBlock[0x1e] = 0x9; + headerBlock[0x20] = 0x6; + headerBlock[0x39] = 0x10; + + // Set the number of BBD blocks + IntegerHelper.getFourBytes(numBigBlockDepotBlocks, + headerBlock, + NUM_BIG_BLOCK_DEPOT_BLOCKS_POS); + + // Set the small block depot chain + IntegerHelper.getFourBytes(sbdStartBlockChain, + headerBlock, + SMALL_BLOCK_DEPOT_BLOCK_POS); + + // Set the number of blocks in the small block depot chain + logger.debug("numSmallBlockDepotChainBlocks " + numSmallBlockDepotChainBlocks); + IntegerHelper.getFourBytes(numSmallBlockDepotChainBlocks, + headerBlock, + NUM_SMALL_BLOCK_DEPOT_BLOCKS_POS); + + // Set the extension block + IntegerHelper.getFourBytes(extensionBlock, + headerBlock, + EXTENSION_BLOCK_POS); + + // Set the number of extension blocks to be the number of BBD blocks - 1 + IntegerHelper.getFourBytes(numExtensionBlocks, + headerBlock, + NUM_EXTENSION_BLOCK_POS); + + // Set the root start block + IntegerHelper.getFourBytes(rootStartBlock, + headerBlock, + ROOT_START_BLOCK_POS); + + // Set the block numbers for the BBD. Set the BBD running + // after the excel data and summary information + int pos = BIG_BLOCK_DEPOT_BLOCKS_POS; + + // See how many blocks fit into the header + int blocksToWrite = Math.min(numBigBlockDepotBlocks, + (BIG_BLOCK_SIZE - + BIG_BLOCK_DEPOT_BLOCKS_POS)/4); + int extensionBlock = 0; + int blocksWritten = 0; + + for (int i = 0 ; i < blocksToWrite; i++) + { + IntegerHelper.getFourBytes(bbdStartBlock + i, + headerBlock, + pos); + pos += 4; + blocksWritten++; + } + + // Pad out the rest of the header with blanks + for (int i = pos; i < BIG_BLOCK_SIZE; i++) + { + headerBlock[i] = (byte) 0xff; + } + + out.write(headerBlock); + + // Write out the extension blocks + pos = 0; + + for (int extBlock = 0; extBlock < numExtensionBlocks; extBlock++) + { + blocksToWrite = Math.min(numBigBlockDepotBlocks - blocksWritten, + BIG_BLOCK_SIZE/4 -1); + + for(int j = 0 ; j < blocksToWrite; j++) + { + IntegerHelper.getFourBytes(bbdStartBlock + blocksWritten + j, + extensionBlockData, + pos); + pos += 4; + } + + blocksWritten += blocksToWrite; + + // Indicate the next block, or the termination of the chain + int nextBlock = (blocksWritten == numBigBlockDepotBlocks) ? + -2 : extBlock+1 ; + IntegerHelper.getFourBytes(nextBlock, extensionBlockData, pos); + pos +=4; + } + + if (numExtensionBlocks > 0) + { + // Pad out the rest of the extension block with blanks + for (int i = pos; i < extensionBlockData.length; i++) + { + extensionBlockData[i] = (byte) 0xff; + } + + out.write(extensionBlockData); + } + } + + /** + * Checks that the data can fit into the current BBD block. If not, + * then it moves on to the next block + * + * @exception IOException + */ + private void checkBbdPos() throws IOException + { + if (bbdPos >= BIG_BLOCK_SIZE) + { + // Write out the extension block. This will simply be the next block + out.write(bigBlockDepot); + + // Create a new block + bigBlockDepot = new byte[BIG_BLOCK_SIZE]; + bbdPos = 0; + } + } + + /** + * Writes out the big block chain + * + * @param startBlock the starting block of the big block chain + * @param numBlocks the number of blocks in the chain + * @exception IOException + */ + private void writeBlockChain(int startBlock, int numBlocks) + throws IOException + { + int blocksToWrite = numBlocks - 1; + int blockNumber = startBlock + 1; + + while (blocksToWrite > 0) + { + int bbdBlocks = Math.min(blocksToWrite, (BIG_BLOCK_SIZE - bbdPos)/4); + + for (int i = 0 ; i < bbdBlocks; i++) + { + IntegerHelper.getFourBytes(blockNumber, bigBlockDepot, bbdPos); + bbdPos +=4 ; + blockNumber++; + } + + blocksToWrite -= bbdBlocks; + checkBbdPos(); + } + + // Write the end of the block chain + IntegerHelper.getFourBytes(-2, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + } + + /** + * Writes the block chains for the additional property sets + * + * @exception IOException + */ + private void writeAdditionalPropertySetBlockChains() throws IOException + { + if (additionalPropertySets == null) + { + return; + } + + int blockNumber = excelDataStartBlock + excelDataBlocks; + for (Iterator i = additionalPropertySets.iterator(); i.hasNext() ; ) + { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + if (rps.data.length > SMALL_BLOCK_THRESHOLD) + { + String psname = rps.propertyStorage.name; + logger.debug("writing big block chain for " + psname); + int numBlocks = getBigBlocksRequired(rps.data.length); + writeBlockChain(blockNumber, numBlocks); + blockNumber += numBlocks; + } + } + } + + /** + * Writes out the chains for the small block depot + */ + private void writeSmallBlockDepotChain() throws IOException + { + if (sbdStartBlockChain == -2) + { + return; + } + + byte[] smallBlockDepotChain = + new byte[numSmallBlockDepotChainBlocks * BIG_BLOCK_SIZE]; + + int pos = 0; + int sbdBlockNumber = 1; + + /* + // First write out the small block chain for the standard property storage + for (int i = 0 ; i < additionalPropertySets.size() + 4; i++) + { + IntegerHelper.getFourBytes(sbdBlockNumber, + smallBlockDepotChain, + pos); + pos += 4; + sbdBlockNumber++; + } + */ + + /* + int numSmallBlocks2 = getSmallBlocksRequired(rootEntryPropertySet.data.length); + for (int j = 0 ; j < numSmallBlocks2 - 1 ; j++) + { + IntegerHelper.getFourBytes(sbdBlockNumber, + smallBlockDepotChain, + pos); + logger.debug("smallBlockChain["+(sbdBlockNumber-1)+"]: " + sbdBlockNumber + " pos " + pos); + pos += 4; + sbdBlockNumber++; + } + */ + + // Write out the end of chain + logger.debug("smallBlockChain["+(sbdBlockNumber-1)+"]: -2"); + IntegerHelper.getFourBytes(-2, smallBlockDepotChain, pos); + pos += 4; + sbdBlockNumber++; + + + for (Iterator i = additionalPropertySets.iterator(); i.hasNext() ; ) + { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + + if (rps.data.length <= SMALL_BLOCK_THRESHOLD && + rps.data.length != 0) + { + int numSmallBlocks = getSmallBlocksRequired(rps.data.length); + for (int j = 0 ; j < numSmallBlocks - 1 ; j++) + { + IntegerHelper.getFourBytes(sbdBlockNumber, + smallBlockDepotChain, + pos); + logger.debug("smallBlockChain["+(sbdBlockNumber-1)+"]: " + sbdBlockNumber + " pos " + pos); + pos += 4; + sbdBlockNumber++; + } + + // Write out the end of chain + logger.debug("smallBlockChain["+(sbdBlockNumber-1)+"]: -2"); + IntegerHelper.getFourBytes(-2, smallBlockDepotChain, pos); + pos += 4; + sbdBlockNumber++; + } + } + + out.write(smallBlockDepotChain); + } + + /** + * Writes out all the data in the small block depot + * + * @exception + */ + private void writeSmallBlockDepot() throws IOException + { + if (additionalPropertySets == null) + { + return; + } + + byte[] smallBlockDepot = + new byte[numSmallBlockDepotBlocks * BIG_BLOCK_SIZE]; + + int pos = 0; + + /* + int smallBlocks2 = getSmallBlocksRequired(rootEntryPropertySet.data.length); + int length2 = smallBlocks2 * SMALL_BLOCK_SIZE; + System.arraycopy(rootEntryPropertySet.data, 0, smallBlockDepot, pos, rootEntryPropertySet.data.length); + pos += length2; + */ + + for (Iterator i = additionalPropertySets.iterator() ; i.hasNext() ; ) + { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + + if (rps.data.length <= SMALL_BLOCK_THRESHOLD) + { + int smallBlocks = getSmallBlocksRequired(rps.data.length); + int length = smallBlocks * SMALL_BLOCK_SIZE; + System.arraycopy(rps.data, 0, smallBlockDepot, pos, rps.data.length); + pos += length; + } + } + + out.write(smallBlockDepot); + } + + /** + * Writes out the Big Block Depot + * + * @exception IOException + */ + private void writeBigBlockDepot() throws IOException + { + // This is after the excel data, the summary information, the + // big block property sets and the small block depot + bigBlockDepot = new byte[BIG_BLOCK_SIZE]; + bbdPos = 0; + + // Write out the extension blocks, indicating them as special blocks + for (int i = 0 ; i < numExtensionBlocks; i++) + { + IntegerHelper.getFourBytes(-3, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + } + + writeBlockChain(excelDataStartBlock, excelDataBlocks); + + // The excel data has been written. Now write out the rest of it + + // Write the block chain for the summary information + int summaryInfoBlock = excelDataStartBlock + + excelDataBlocks + + additionalPropertyBlocks; + + for (int i = summaryInfoBlock; i < summaryInfoBlock + 7; i++) + { + IntegerHelper.getFourBytes(i + 1, bigBlockDepot, bbdPos); + bbdPos +=4 ; + checkBbdPos(); + } + + // Write the end of the block chain for the summary info block + IntegerHelper.getFourBytes(-2, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + + // Write the block chain for the document summary information + for (int i = summaryInfoBlock + 8; i < summaryInfoBlock + 15; i++) + { + IntegerHelper.getFourBytes(i + 1, bigBlockDepot, bbdPos); + bbdPos +=4 ; + checkBbdPos(); + } + + // Write the end of the block chain for the document summary + IntegerHelper.getFourBytes(-2, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + + // Write out the block chain for the copied property sets, if present + writeAdditionalPropertySetBlockChains(); + + // Write out the block chain for the small block depot + writeBlockChain(sbdStartBlock, numSmallBlockDepotBlocks); + + // Write out the block chain for the small block depot chain + logger.debug("numSmallBlockDepotChainBlocks " + numSmallBlockDepotChainBlocks); + writeBlockChain(sbdStartBlockChain, numSmallBlockDepotChainBlocks); + + + // The Big Block Depot immediately follows the document summary. Denote + // these as a special block + for (int i = 0; i < numBigBlockDepotBlocks; i++) + { + IntegerHelper.getFourBytes(-3, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + } + + // Write the root entry + writeBlockChain(rootStartBlock, numRootEntryBlocks); + + // Pad out the remainder of the block + if (bbdPos != 0) + { + for (int i = bbdPos; i < BIG_BLOCK_SIZE; i++) + { + bigBlockDepot[i] = (byte) 0xff; + } + out.write(bigBlockDepot); + } + } + + /** + * Calculates the number of big blocks required to store data of the + * specified length + * + * @param length the length of the data + * @return the number of big blocks required to store the data + */ + private int getBigBlocksRequired(int length) + { + int blocks = length / BIG_BLOCK_SIZE; + + return (length % BIG_BLOCK_SIZE > 0 )? blocks + 1 : blocks; + } + + /** + * Calculates the number of small blocks required to store data of the + * specified length + * + * @param length the length of the data + * @return the number of small blocks required to store the data + */ + private int getSmallBlocksRequired(int length) + { + int blocks = length / SMALL_BLOCK_SIZE; + + return (length % SMALL_BLOCK_SIZE > 0 )? blocks + 1 : blocks; + } + + /** + * Writes out the property sets + * + * @exception IOException + */ + private void writePropertySets() throws IOException + { + byte[] propertySetStorage = new byte[BIG_BLOCK_SIZE * numRootEntryBlocks]; + + int pos = 0; + int[] mappings = null; + + // Build up the mappings array + if (additionalPropertySets != null) + { + mappings = new int[numPropertySets]; + + // Map the standard ones to the first four + for (int i = 0 ; i < STANDARD_PROPERTY_SETS.length ; i++) + { + ReadPropertyStorage rps = (ReadPropertyStorage) + readPropertySets.get(STANDARD_PROPERTY_SETS[i]); + mappings[rps.number] = i; + } + + // Now go through the original ones + int newMapping = STANDARD_PROPERTY_SETS.length; + for (Iterator i = additionalPropertySets.iterator(); i.hasNext(); ) + { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + mappings[rps.number] = newMapping; + newMapping++; + } + } + + int dir = 0; + int previous = 0; + int next = 0; + + // Compute the size of the root property set + int size = 0; + + if (additionalPropertySets != null) + { + // Workbook + size += getBigBlocksRequired(requiredSize) * BIG_BLOCK_SIZE; + + // The two information blocks + size += getBigBlocksRequired(SMALL_BLOCK_THRESHOLD) * BIG_BLOCK_SIZE; + size += getBigBlocksRequired(SMALL_BLOCK_THRESHOLD) * BIG_BLOCK_SIZE; + + // Additional property sets + for (Iterator i = additionalPropertySets.iterator(); i.hasNext(); ) + { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + if (rps.propertyStorage.size >= SMALL_BLOCK_THRESHOLD) + { + size += getBigBlocksRequired(rps.propertyStorage.size) * + BIG_BLOCK_SIZE; + } + else + { + size += getSmallBlocksRequired(rps.propertyStorage.size) * + SMALL_BLOCK_SIZE; + } + } + } + logger.debug("Root entry size is " + size); + + // Set the root entry property set + PropertyStorage ps = new PropertyStorage(ROOT_ENTRY_NAME); + ps.setType(5); + ps.setStartBlock(sbdStartBlock); + ps.setSize(size); + ps.setPrevious(-1); + ps.setNext(-1); + ps.setColour(0); + + dir = 2; + if (additionalPropertySets != null) + { + ReadPropertyStorage rps = (ReadPropertyStorage) + readPropertySets.get(ROOT_ENTRY_NAME); + dir = mappings[rps.propertyStorage.directory]; + } + ps.setDirectory(dir); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + + // Set the workbook property set + ps = new PropertyStorage(WORKBOOK_NAME); + ps.setType(2); + ps.setStartBlock(excelDataStartBlock); + // start the excel data after immediately after this block + ps.setSize(requiredSize); + // alway use a big block stream - none of that messing around + // with small blocks + + previous = 3; + + if (additionalPropertySets != null) + { + ReadPropertyStorage rps = (ReadPropertyStorage) + readPropertySets.get(WORKBOOK_NAME); + previous = mappings[rps.propertyStorage.previous]; + } + + ps.setPrevious(previous); + ps.setNext(-1); + ps.setDirectory(-1); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + // Set the summary information + ps = new PropertyStorage(SUMMARY_INFORMATION_NAME); + ps.setType(2); + ps.setStartBlock(excelDataStartBlock + excelDataBlocks); + ps.setSize(SMALL_BLOCK_THRESHOLD); + + previous = 1; + next = 3; + + if (additionalPropertySets != null) + { + ReadPropertyStorage rps = (ReadPropertyStorage) + readPropertySets.get(SUMMARY_INFORMATION_NAME); + + previous = rps.propertyStorage.previous != - 1 ? + mappings[rps.propertyStorage.previous] : -1 ; + next = rps.propertyStorage.next != - 1 ? + mappings[rps.propertyStorage.next] : -1 ; + } + + ps.setPrevious(previous); + ps.setNext(next); + ps.setDirectory(-1); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + // Set the document summary information + ps = new PropertyStorage(DOCUMENT_SUMMARY_INFORMATION_NAME); + ps.setType(2); + ps.setStartBlock(excelDataStartBlock + excelDataBlocks + 8); + ps.setSize(SMALL_BLOCK_THRESHOLD); + ps.setPrevious(-1); + ps.setNext(-1); + ps.setDirectory(-1); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + + + // Write out the additional property sets + if (additionalPropertySets == null) + { + out.write(propertySetStorage); + return; + } + + int bigBlock = excelDataStartBlock + excelDataBlocks + 16; + int smallBlock = 0;//getSmallBlocksRequired(rootEntryPropertySet.data.length); + int sz = 0; + + for (Iterator i = additionalPropertySets.iterator() ; i.hasNext(); ) + { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + + int block = rps.data.length > SMALL_BLOCK_THRESHOLD ? + bigBlock : smallBlock; + + ps = new PropertyStorage(rps.propertyStorage.name); + ps.setType(rps.propertyStorage.type); + ps.setStartBlock(block); + ps.setSize(rps.propertyStorage.size); + // ps.setColour(rps.propertyStorage.colour); + + previous = rps.propertyStorage.previous != -1 ? + mappings[rps.propertyStorage.previous] : -1; + next = rps.propertyStorage.next != -1 ? + mappings[rps.propertyStorage.next] : -1; + dir = rps.propertyStorage.directory != -1 ? + mappings[rps.propertyStorage.directory] : -1; + + ps.setPrevious(previous); + ps.setNext(next); + ps.setDirectory(dir); + logger.debug("ps.colour " + ps.colour); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + if (rps.data.length > SMALL_BLOCK_THRESHOLD) + { + bigBlock += getBigBlocksRequired(rps.data.length); + } + else + { + smallBlock += getSmallBlocksRequired(rps.data.length); + } + sz += getSmallBlocksRequired(rps.propertyStorage.size); + } + + out.write(propertySetStorage); + } +} + + + + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/CompoundFile.java3 =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/CompoundFile.java3,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/CompoundFile.java3 17 Aug 2012 14:51:10 -0000 1.1 @@ -0,0 +1,1020 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import java.io.OutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.HashMap; + +import common.Assert; +import common.Logger; +import jxl.biff.BaseCompoundFile; +import jxl.biff.IntegerHelper; +import jxl.read.biff.BiffException; + +/** + * Writes out a compound file + * + * Header block is -1 + * Excel data is e..n (where is the head extension blocks, normally 0 and + * n is at least 8) + * Summary information (8 blocks) + * Document summary (8 blocks) + * BBD is block p..q (where p=e+n+16 and q-p+1 is the number of BBD blocks) + * Property storage block is q+b...r (normally 1 block) (where b is the number + * of BBD blocks) + */ +final class CompoundFile extends BaseCompoundFile +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(CompoundFile.class); + + /** + * The stream to which the jumbled up data is written to + */ + private OutputStream out; + /** + * The organized biff records which form the actual excel data + */ + private byte[] excelData; + + /** + * The size of the array + */ + private int size; + + /** + * The size the excel data should be in order to comply with the + * general compound file format + */ + private int requiredSize; + + /** + * The number of blocks it takes to store the big block depot + */ + private int numBigBlockDepotBlocks; + + /** + * The number of blocks it takes to store the small block depot chain + */ + private int numSmallBlockDepotChainBlocks; + + /** + * The number of blocks it takes to store the small block depot + */ + private int numSmallBlockDepotBlocks; + + /** + * The number of extension blocks required for the header to describe + * the BBD + */ + private int numExtensionBlocks; + + /** + * The extension block for the header + */ + private int extensionBlock; + + /** + * The number of blocks it takes to store the excel data + */ + private int excelDataBlocks; + + /** + * The start block of the root entry + */ + private int rootStartBlock; + + /** + * The start block of the excel data + */ + private int excelDataStartBlock; + + /** + * The start block of the big block depot + */ + private int bbdStartBlock; + + /** + * The number of big blocks required for additional property sets + */ + private int additionalPropertyBlocks; + + /** + * The total number of property sets in this compound file + */ + private int numPropertySets; + + /** + * The number of blocks required to store the root entry property sets + * and small block depot + */ + private int numRootEntryBlocks; + + /** + * The list of additional, non standard property sets names + */ + private ArrayList additionalPropertySets; + + /** + * A hash map of the original property sets keyed on name + */ + private HashMap readPropertySets; + + /** + * The array of standard property set mappings + */ + private int[] standardPropertySetMappings; + + private ReadPropertyStorage rootEntryPropertySet; + + /** + * Structure used to store the property set and the data + */ + private static final class ReadPropertyStorage + { + PropertyStorage propertyStorage; + byte[] data; + int number; + + ReadPropertyStorage(PropertyStorage ps, byte[] d, int n) + { + propertyStorage = ps; + data = d; + number = n; + } + } + + + // The following member variables are used across methods when + // writing out the big block depot + /** + * The current position within the bbd. Used when writing out the + * BBD + */ + private int bbdPos; + + /** + * The current bbd block + */ + private byte[] bigBlockDepot; + + + /** + * Constructor + * + * @param l the length of the data + * @param os the output stream to write to + * @param data the excel data + * @param rcf the read compound + */ + public CompoundFile(byte[] data, int l, OutputStream os, + jxl.read.biff.CompoundFile rcf) + throws CopyAdditionalPropertySetsException, IOException + { + super(); + size = l; + excelData = data; + + readAdditionalPropertySets(rcf); + + numRootEntryBlocks = 1; + numPropertySets = 4 + + (additionalPropertySets != null ? additionalPropertySets.size() : 0); + + if (additionalPropertySets != null) + { + try + { + rootEntryPropertySet = new ReadPropertyStorage(rcf.getPropertySet(ROOT_ENTRY_NAME), rcf.getStream(ROOT_ENTRY_NAME), 0); + int blocks = rootEntryPropertySet.data.length >= SMALL_BLOCK_THRESHOLD ? + getBigBlocksRequired(rootEntryPropertySet.data.length) : + SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + additionalPropertyBlocks += blocks; + } + catch(BiffException e) + { + e.printStackTrace(); + } + + numRootEntryBlocks += getBigBlocksRequired + (additionalPropertySets.size() * PROPERTY_STORAGE_BLOCK_SIZE); + } + + logger.debug("root entry requires " + numRootEntryBlocks + " blocks"); + + int blocks = getBigBlocksRequired(l); + + // First pad the data out so that it fits nicely into a whole number + // of blocks + if (l < SMALL_BLOCK_THRESHOLD) + { + requiredSize = SMALL_BLOCK_THRESHOLD; + } + else + { + requiredSize = blocks * BIG_BLOCK_SIZE; + } + + out = os; + + // logger.debug("smallBlockDepot requires " + numSmallBlockDepotBlocks + " big blocks"); + + // Do the calculations + excelDataBlocks = requiredSize/BIG_BLOCK_SIZE; + numBigBlockDepotBlocks = 1; + + int blockChainLength = (BIG_BLOCK_SIZE - BIG_BLOCK_DEPOT_BLOCKS_POS)/4; + + int startTotalBlocks = excelDataBlocks + + 8 + // summary block + 8 + // document information + additionalPropertyBlocks + + numRootEntryBlocks; + + int totalBlocks = startTotalBlocks + numBigBlockDepotBlocks; + + // Calculate the number of BBD blocks needed to hold this info + numBigBlockDepotBlocks = (int) Math.ceil( (double) totalBlocks / + (double) (BIG_BLOCK_SIZE/4)); + + // Does this affect the total? + totalBlocks = startTotalBlocks + numBigBlockDepotBlocks; + + // And recalculate + numBigBlockDepotBlocks = (int) Math.ceil( (double) totalBlocks / + (double) (BIG_BLOCK_SIZE/4)); + + // Does this affect the total? + totalBlocks = startTotalBlocks + numBigBlockDepotBlocks; + + // See if the excel bbd chain can fit into the header block. + // Remember to allow for the end of chain indicator + if (numBigBlockDepotBlocks > blockChainLength - 1 ) + { + // Sod it - we need an extension block. We have to go through + // the whole tiresome calculation again + extensionBlock = 0; + + // Compute the number of extension blocks + int bbdBlocksLeft = numBigBlockDepotBlocks - blockChainLength + 1; + + numExtensionBlocks = (int) Math.ceil((double) bbdBlocksLeft / + (double) (BIG_BLOCK_SIZE/4 - 1)); + + // Modify the total number of blocks required and recalculate the + // the number of bbd blocks + totalBlocks = startTotalBlocks + + numExtensionBlocks + + numBigBlockDepotBlocks; + numBigBlockDepotBlocks = (int) Math.ceil( (double) totalBlocks / + (double) (BIG_BLOCK_SIZE/4)); + + // The final total + totalBlocks = startTotalBlocks + + numExtensionBlocks + + numBigBlockDepotBlocks; + } + else + { + extensionBlock = -2; + numExtensionBlocks = 0; + } + + // Set the excel data start block to be after the header (and + // its extensions) + excelDataStartBlock = numExtensionBlocks; + + logger.debug("excelDataStartBlock " + excelDataStartBlock); + + // Set the bbd start block to be after all the excel data + bbdStartBlock = excelDataStartBlock + + excelDataBlocks + + additionalPropertyBlocks + + 16; + + logger.debug("bbdStartBlock " + bbdStartBlock); + + // Set the root start block to be after all the big block depot blocks + rootStartBlock = bbdStartBlock + + numBigBlockDepotBlocks; + + + if (totalBlocks != rootStartBlock + numRootEntryBlocks) + { + logger.warn("Root start block and total blocks are inconsistent " + + " generated file may be corrupt"); + logger.warn("RootStartBlock " + rootStartBlock + " totalBlocks " + totalBlocks); + } + } + + /** + * Reads the additional property sets from the read in compound file + * + * @return the number of blocks needed to store these property sets + */ + private void readAdditionalPropertySets + (jxl.read.biff.CompoundFile readCompoundFile) + throws CopyAdditionalPropertySetsException, IOException + { + if (readCompoundFile == null) + { + return; + } + + additionalPropertySets = new ArrayList(); + readPropertySets = new HashMap(); + + String[] psnames = readCompoundFile.getPropertySetNames(); + int blocksRequired = 0; + standardPropertySetMappings = new int[STANDARD_PROPERTY_SETS.length]; + + for (int i = 0 ; i < psnames.length ; i++) + { + // Add it to the hash map for later + PropertyStorage ps = readCompoundFile.getPropertySet(psnames[i]); + + // If the name is non standard, then retrieve the property set + // information + boolean standard = false; + for (int j = 0 ; j < STANDARD_PROPERTY_SETS.length && !standard ; j++) + { + if (psnames[i].equalsIgnoreCase(STANDARD_PROPERTY_SETS[j])) + { + standard = true; + ReadPropertyStorage rps = new ReadPropertyStorage(ps, null, i); + readPropertySets.put(psnames[i], rps); + } + } + + if (!standard) + { + try + { + byte[] data = null; + if (ps.size > 0 ) + { + data = readCompoundFile.getStream(ps.name); + } + else + { + data = new byte[0]; + } + ReadPropertyStorage rps = new ReadPropertyStorage(ps, data, i); + readPropertySets.put(psnames[i], rps); + additionalPropertySets.add(rps); + + int blocks = data.length >= SMALL_BLOCK_THRESHOLD ? + getBigBlocksRequired(data.length) : + SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + blocksRequired += blocks; + } + catch (BiffException e) + { + logger.error(e); + throw new CopyAdditionalPropertySetsException(); + } + } + } + + additionalPropertyBlocks = blocksRequired; + } + + + /** + * Writes out the excel file in OLE compound file format + * + * @exception IOException + */ + public void write() throws IOException + { + writeHeader(); + writeExcelData(); + writeDocumentSummaryData(); + writeSummaryData(); + writeAdditionalPropertySets(); + writeBigBlockDepot(); + writePropertySets(); + + // Don't flush or close the stream - this is handled by the enclosing File + // object + } + + /** + * Writes out any additional property sets + */ + private void writeAdditionalPropertySets() throws IOException + { + if (additionalPropertySets == null) + { + return; + } + + + logger.debug("Writing property set " + rootEntryPropertySet.propertyStorage.name); + int numBlocks2 = rootEntryPropertySet.data.length >= SMALL_BLOCK_THRESHOLD ? + getBigBlocksRequired(rootEntryPropertySet.data.length) : + SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + int requiredSize2 = numBlocks2 * BIG_BLOCK_SIZE; + + out.write(rootEntryPropertySet.data, 0, rootEntryPropertySet.data.length); + + byte[] padding2 = new byte[requiredSize2 - rootEntryPropertySet.data.length]; + out.write(padding2, 0, padding2.length); + + logger.debug("data length " + rootEntryPropertySet.data.length + " Padding " + padding2.length); + + for (Iterator i = additionalPropertySets.iterator(); i.hasNext() ;) + { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + byte[] data = rps.data; + + logger.debug("Writing property set " + rps.propertyStorage.name); + int numBlocks = data.length >= SMALL_BLOCK_THRESHOLD ? + getBigBlocksRequired(data.length) : + SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + int requiredSize = numBlocks * BIG_BLOCK_SIZE; + + out.write(data, 0, data.length); + + byte[] padding = new byte[requiredSize - data.length]; + out.write(padding, 0, padding.length); + } + } + + /** + * Writes out the excel data, padding it out with empty bytes as + * necessary + * Also write out empty + * + * @exception IOException + */ + private void writeExcelData() throws IOException + { + logger.debug("num excel data blocks " + excelDataBlocks + " excelData size " + requiredSize); + out.write(excelData, 0, size); + + byte[] padding = new byte[requiredSize - size]; + out.write(padding); + } + + /** + * Write out the document summary data. This is just blank + * + * @exception IOException + */ + private void writeDocumentSummaryData() throws IOException + { + byte[] padding = new byte[SMALL_BLOCK_THRESHOLD]; + + // Write out the summary information + out.write(padding); + } + + /** + * Write out the summary data. This is just blank + * + * @exception IOException + */ + private void writeSummaryData() throws IOException + { + byte[] padding = new byte[SMALL_BLOCK_THRESHOLD]; + + // Write out the summary information + out.write(padding); + } + + /** + * Writes the compound file header + * + * @exception IOException + */ + private void writeHeader() throws IOException + { + logger.debug("num extensions blocks for header: " + numExtensionBlocks); + // Build up the header array + byte[] headerBlock = new byte[BIG_BLOCK_SIZE]; + byte[] extensionBlockData = new byte[BIG_BLOCK_SIZE * numExtensionBlocks]; + + // Copy in the identifier + System.arraycopy(IDENTIFIER, 0, headerBlock, 0, IDENTIFIER.length); + + // Copy in some magic values - no idea what they mean + headerBlock[0x18] = 0x3e; + headerBlock[0x1a] = 0x3; + headerBlock[0x1c] = (byte) 0xfe; + headerBlock[0x1d] = (byte) 0xff; + headerBlock[0x1e] = 0x9; + headerBlock[0x20] = 0x6; + headerBlock[0x39] = 0x10; + + // Set the number of BBD blocks + IntegerHelper.getFourBytes(numBigBlockDepotBlocks, + headerBlock, + NUM_BIG_BLOCK_DEPOT_BLOCKS_POS); + + // Set the small block depot chain to -2 ie. no small block chain + IntegerHelper.getFourBytes(-2, + headerBlock, + SMALL_BLOCK_DEPOT_BLOCK_POS); + + // Set the extension block + IntegerHelper.getFourBytes(extensionBlock, + headerBlock, + EXTENSION_BLOCK_POS); + + // Set the number of extension blocks to be the number of BBD blocks - 1 + IntegerHelper.getFourBytes(numExtensionBlocks, + headerBlock, + NUM_EXTENSION_BLOCK_POS); + + // Set the root start block + IntegerHelper.getFourBytes(rootStartBlock, + headerBlock, + ROOT_START_BLOCK_POS); + + // Set the block numbers for the BBD. Set the BBD running + // after the excel data and summary information + int pos = BIG_BLOCK_DEPOT_BLOCKS_POS; + + // See how many blocks fit into the header + int blocksToWrite = Math.min(numBigBlockDepotBlocks, + (BIG_BLOCK_SIZE - + BIG_BLOCK_DEPOT_BLOCKS_POS)/4); + int extensionBlock = 0; + int blocksWritten = 0; + + for (int i = 0 ; i < blocksToWrite; i++) + { + IntegerHelper.getFourBytes(bbdStartBlock + i, + headerBlock, + pos); + pos += 4; + blocksWritten++; + } + + // Pad out the rest of the header with blanks + for (int i = pos; i < BIG_BLOCK_SIZE; i++) + { + headerBlock[i] = (byte) 0xff; + } + + out.write(headerBlock); + + // Write out the extension blocks + pos = 0; + + for (int extBlock = 0; extBlock < numExtensionBlocks; extBlock++) + { + blocksToWrite = Math.min(numBigBlockDepotBlocks - blocksWritten, + BIG_BLOCK_SIZE/4 -1); + + for(int j = 0 ; j < blocksToWrite; j++) + { + IntegerHelper.getFourBytes(bbdStartBlock + blocksWritten + j, + extensionBlockData, + pos); + pos += 4; + } + + blocksWritten += blocksToWrite; + + // Indicate the next block, or the termination of the chain + int nextBlock = (blocksWritten == numBigBlockDepotBlocks) ? + -2 : extBlock+1 ; + IntegerHelper.getFourBytes(nextBlock, extensionBlockData, pos); + pos +=4; + } + + if (numExtensionBlocks > 0) + { + // Pad out the rest of the extension block with blanks + for (int i = pos; i < extensionBlockData.length; i++) + { + extensionBlockData[i] = (byte) 0xff; + } + + out.write(extensionBlockData); + } + } + + /** + * Checks that the data can fit into the current BBD block. If not, + * then it moves on to the next block + * + * @exception IOException + */ + private void checkBbdPos() throws IOException + { + if (bbdPos >= BIG_BLOCK_SIZE) + { + // Write out the extension block. This will simply be the next block + out.write(bigBlockDepot); + + // Create a new block + bigBlockDepot = new byte[BIG_BLOCK_SIZE]; + bbdPos = 0; + } + } + + /** + * Writes out the big block chain + * + * @param startBlock the starting block of the big block chain + * @param numBlocks the number of blocks in the chain + * @exception IOException + */ + private void writeBlockChain(int startBlock, int numBlocks) + throws IOException + { + int blocksToWrite = numBlocks - 1; + int blockNumber = startBlock + 1; + + while (blocksToWrite > 0) + { + int bbdBlocks = Math.min(blocksToWrite, (BIG_BLOCK_SIZE - bbdPos)/4); + + for (int i = 0 ; i < bbdBlocks; i++) + { + IntegerHelper.getFourBytes(blockNumber, bigBlockDepot, bbdPos); + bbdPos +=4 ; + blockNumber++; + } + + blocksToWrite -= bbdBlocks; + checkBbdPos(); + } + + // Write the end of the block chain + IntegerHelper.getFourBytes(-2, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + } + + /** + * Writes the block chains for the additional property sets + * + * @exception IOException + */ + private void writeAdditionalPropertySetBlockChains() throws IOException + { + if (additionalPropertySets == null) + { + return; + } + + int blockNumber = excelDataStartBlock + excelDataBlocks + 16; + + int numBlocks2 = rootEntryPropertySet.data.length >= SMALL_BLOCK_THRESHOLD ? + getBigBlocksRequired(rootEntryPropertySet.data.length) : + SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + + String psname2 = rootEntryPropertySet.propertyStorage.name; + logger.debug("writing big block chain for " + psname2 + " block " + blockNumber + " numBlocks " + numBlocks2); + writeBlockChain(blockNumber, numBlocks2); + blockNumber += numBlocks2; + + for (Iterator i = additionalPropertySets.iterator(); i.hasNext() ; ) + { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + + int numBlocks = rps.data.length >= SMALL_BLOCK_THRESHOLD ? + getBigBlocksRequired(rps.data.length) : + SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + + String psname = rps.propertyStorage.name; + logger.debug("writing big block chain for " + psname + " block " + blockNumber + " numBlocks " + numBlocks); + writeBlockChain(blockNumber, numBlocks); + blockNumber += numBlocks; + } + } + + /** + * Writes out the Big Block Depot + * + * @exception IOException + */ + private void writeBigBlockDepot() throws IOException + { + // This is after the excel data, the summary information, the + // big block property sets and the small block depot + bigBlockDepot = new byte[BIG_BLOCK_SIZE]; + bbdPos = 0; + + // Write out the extension blocks, indicating them as special blocks + for (int i = 0 ; i < numExtensionBlocks; i++) + { + IntegerHelper.getFourBytes(-3, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + } + + writeBlockChain(excelDataStartBlock, excelDataBlocks); + + // The excel data has been written. Now write out the rest of it + + // Write the block chain for the summary information + int summaryInfoBlock = excelDataStartBlock + + excelDataBlocks + + additionalPropertyBlocks; + + for (int i = summaryInfoBlock; i < summaryInfoBlock + 7; i++) + { + IntegerHelper.getFourBytes(i + 1, bigBlockDepot, bbdPos); + bbdPos +=4 ; + checkBbdPos(); + } + + // Write the end of the block chain for the summary info block + IntegerHelper.getFourBytes(-2, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + + // Write the block chain for the document summary information + for (int i = summaryInfoBlock + 8; i < summaryInfoBlock + 15; i++) + { + IntegerHelper.getFourBytes(i + 1, bigBlockDepot, bbdPos); + bbdPos +=4 ; + checkBbdPos(); + } + + // Write the end of the block chain for the document summary + IntegerHelper.getFourBytes(-2, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + + // Write out the block chain for the copied property sets, if present + writeAdditionalPropertySetBlockChains(); + + // The Big Block Depot immediately follows the document summary. Denote + // these as a special block + for (int i = 0; i < numBigBlockDepotBlocks; i++) + { + IntegerHelper.getFourBytes(-3, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + } + + // Write the root entry + writeBlockChain(rootStartBlock, numRootEntryBlocks); + + // Pad out the remainder of the block + if (bbdPos != 0) + { + for (int i = bbdPos; i < BIG_BLOCK_SIZE; i++) + { + bigBlockDepot[i] = (byte) 0xff; + } + out.write(bigBlockDepot); + } + } + + /** + * Calculates the number of big blocks required to store data of the + * specified length + * + * @param length the length of the data + * @return the number of big blocks required to store the data + */ + private int getBigBlocksRequired(int length) + { + int blocks = length / BIG_BLOCK_SIZE; + + return (length % BIG_BLOCK_SIZE > 0 )? blocks + 1 : blocks; + } + + /** + * Calculates the number of small blocks required to store data of the + * specified length + * + * @param length the length of the data + * @return the number of small blocks required to store the data + */ + private int getSmallBlocksRequired(int length) + { + int blocks = length / SMALL_BLOCK_SIZE; + + return (length % SMALL_BLOCK_SIZE > 0 )? blocks + 1 : blocks; + } + + /** + * Writes out the property sets + * + * @exception IOException + */ + private void writePropertySets() throws IOException + { + byte[] propertySetStorage = new byte[BIG_BLOCK_SIZE * numRootEntryBlocks]; + + int pos = 0; + int[] mappings = null; + + // Build up the mappings array + if (additionalPropertySets != null) + { + mappings = new int[numPropertySets]; + + // Map the standard ones to the first four + for (int i = 0 ; i < STANDARD_PROPERTY_SETS.length ; i++) + { + ReadPropertyStorage rps = (ReadPropertyStorage) + readPropertySets.get(STANDARD_PROPERTY_SETS[i]); + mappings[rps.number] = i; + } + + // Now go through the original ones + int newMapping = STANDARD_PROPERTY_SETS.length; + for (Iterator i = additionalPropertySets.iterator(); i.hasNext(); ) + { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + mappings[rps.number] = newMapping; + newMapping++; + } + } + + int dir = 0; + int previous = 0; + int next = 0; + + // Set the root entry property set + PropertyStorage ps = new PropertyStorage(ROOT_ENTRY_NAME); + ps.setType(5); + ps.setStartBlock(-2); + ps.setSize(0); + ps.setPrevious(-1); + ps.setNext(-1); + ps.setColour(0); + + dir = 2; + if (additionalPropertySets != null) + { + ReadPropertyStorage rps = (ReadPropertyStorage) + readPropertySets.get(ROOT_ENTRY_NAME); + dir = mappings[rps.propertyStorage.directory]; + } + ps.setDirectory(dir); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + + // Set the workbook property set + ps = new PropertyStorage(WORKBOOK_NAME); + ps.setType(2); + ps.setStartBlock(excelDataStartBlock); + // start the excel data after immediately after this block + ps.setSize(requiredSize); + // alway use a big block stream - none of that messing around + // with small blocks + ps.setColour(1); + + previous = 3; + + if (additionalPropertySets != null) + { + ReadPropertyStorage rps = (ReadPropertyStorage) + readPropertySets.get(WORKBOOK_NAME); + previous = mappings[rps.propertyStorage.previous]; + } + + ps.setPrevious(previous); + ps.setNext(-1); + ps.setDirectory(-1); + ps.setColour(1); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + // Set the summary information + ps = new PropertyStorage(SUMMARY_INFORMATION_NAME); + ps.setType(2); + ps.setStartBlock(excelDataStartBlock + excelDataBlocks); + ps.setSize(SMALL_BLOCK_THRESHOLD); + ps.setColour(1); + + previous = 1; + next = 3; + + if (additionalPropertySets != null) + { + ReadPropertyStorage rps = (ReadPropertyStorage) + readPropertySets.get(SUMMARY_INFORMATION_NAME); + + previous = rps.propertyStorage.previous != - 1 ? + mappings[rps.propertyStorage.previous] : -1 ; + next = rps.propertyStorage.next != - 1 ? + mappings[rps.propertyStorage.next] : -1 ; + } + + ps.setPrevious(previous); + ps.setNext(next); + ps.setDirectory(-1); + ps.setColour(1); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + // Set the document summary information + ps = new PropertyStorage(DOCUMENT_SUMMARY_INFORMATION_NAME); + ps.setType(2); + ps.setStartBlock(excelDataStartBlock + excelDataBlocks + 8); + ps.setSize(SMALL_BLOCK_THRESHOLD); + ps.setPrevious(-1); + ps.setNext(-1); + ps.setDirectory(-1); + ps.setColour(1); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + + + // Write out the additional property sets + if (additionalPropertySets == null) + { + out.write(propertySetStorage); + return; + } + + int bigBlock = excelDataStartBlock + excelDataBlocks + 16 + + 18; + + for (Iterator i = additionalPropertySets.iterator() ; i.hasNext(); ) + { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + + ps = new PropertyStorage(rps.propertyStorage.name); + ps.setType(rps.propertyStorage.type); + ps.setStartBlock(bigBlock); + ps.setSize(Math.max(rps.propertyStorage.size, SMALL_BLOCK_THRESHOLD)); + + previous = rps.propertyStorage.previous != -1 ? + mappings[rps.propertyStorage.previous] : -1; + next = rps.propertyStorage.next != -1 ? + mappings[rps.propertyStorage.next] : -1; + dir = rps.propertyStorage.directory != -1 ? + mappings[rps.propertyStorage.directory] : -1; + + ps.setPrevious(previous); + ps.setNext(next); + ps.setDirectory(dir); + ps.setColour(1); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + + if (rps.data.length >= SMALL_BLOCK_THRESHOLD) + { + bigBlock += getBigBlocksRequired(rps.data.length); + } + else + { + bigBlock += SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + } + } + + out.write(propertySetStorage); + } +} + + + + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/CompoundFile.java4 =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/CompoundFile.java4,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/CompoundFile.java4 17 Aug 2012 14:51:10 -0000 1.1 @@ -0,0 +1,1025 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import java.io.OutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.HashMap; + +import common.Assert; +import common.Logger; +import jxl.biff.BaseCompoundFile; +import jxl.biff.IntegerHelper; +import jxl.read.biff.BiffException; + +/** + * Writes out a compound file + * + * Header block is -1 + * Excel data is e..n (where is the head extension blocks, normally 0 and + * n is at least 8) + * Summary information (8 blocks) + * Document summary (8 blocks) + * BBD is block p..q (where p=e+n+16 and q-p+1 is the number of BBD blocks) + * Property storage block is q+b...r (normally 1 block) (where b is the number + * of BBD blocks) + */ +final class CompoundFile extends BaseCompoundFile +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(CompoundFile.class); + + /** + * The stream to which the jumbled up data is written to + */ + private OutputStream out; + /** + * The organized biff records which form the actual excel data + */ + private byte[] excelData; + + /** + * The size of the array + */ + private int size; + + /** + * The size the excel data should be in order to comply with the + * general compound file format + */ + private int requiredSize; + + /** + * The number of blocks it takes to store the big block depot + */ + private int numBigBlockDepotBlocks; + + /** + * The number of blocks it takes to store the small block depot chain + */ + private int numSmallBlockDepotChainBlocks; + + /** + * The number of blocks it takes to store the small block depot + */ + private int numSmallBlockDepotBlocks; + + /** + * The number of extension blocks required for the header to describe + * the BBD + */ + private int numExtensionBlocks; + + /** + * The extension block for the header + */ + private int extensionBlock; + + /** + * The number of blocks it takes to store the excel data + */ + private int excelDataBlocks; + + /** + * The start block of the root entry + */ + private int rootStartBlock; + + /** + * The start block of the excel data + */ + private int excelDataStartBlock; + + /** + * The start block of the big block depot + */ + private int bbdStartBlock; + + /** + * The number of big blocks required for additional property sets + */ + private int additionalPropertyBlocks; + + /** + * The total number of property sets in this compound file + */ + private int numPropertySets; + + /** + * The number of blocks required to store the root entry property sets + * and small block depot + */ + private int numRootEntryBlocks; + + /** + * The list of additional, non standard property sets names + */ + private ArrayList additionalPropertySets; + + /** + * A hash map of the original property sets keyed on name + */ + private HashMap readPropertySets; + + /** + * The array of standard property set mappings + */ + private int[] standardPropertySetMappings; + + private ReadPropertyStorage rootEntryPropertySet; + + /** + * Structure used to store the property set and the data + */ + private static final class ReadPropertyStorage + { + PropertyStorage propertyStorage; + byte[] data; + int number; + + ReadPropertyStorage(PropertyStorage ps, byte[] d, int n) + { + propertyStorage = ps; + data = d; + number = n; + } + } + + + // The following member variables are used across methods when + // writing out the big block depot + /** + * The current position within the bbd. Used when writing out the + * BBD + */ + private int bbdPos; + + /** + * The current bbd block + */ + private byte[] bigBlockDepot; + + + /** + * Constructor + * + * @param l the length of the data + * @param os the output stream to write to + * @param data the excel data + * @param rcf the read compound + */ + public CompoundFile(byte[] data, int l, OutputStream os, + jxl.read.biff.CompoundFile rcf) + throws CopyAdditionalPropertySetsException, IOException + { + super(); + size = l; + excelData = data; + + readAdditionalPropertySets(rcf); + + numRootEntryBlocks = 1; + numPropertySets = 4 + + (additionalPropertySets != null ? additionalPropertySets.size() : 0); + + if (additionalPropertySets != null) + { + /* + try + { + rootEntryPropertySet = new ReadPropertyStorage(rcf.getPropertySet(ROOT_ENTRY_NAME), rcf.getStream(ROOT_ENTRY_NAME), 0); + int blocks = rootEntryPropertySet.data.length >= SMALL_BLOCK_THRESHOLD ? + getBigBlocksRequired(rootEntryPropertySet.data.length) : + SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + additionalPropertyBlocks += blocks; + } + catch(BiffException e) + { + e.printStackTrace(); + } + */ + numRootEntryBlocks += getBigBlocksRequired + (additionalPropertySets.size() * PROPERTY_STORAGE_BLOCK_SIZE); + } + + logger.debug("root entry requires " + numRootEntryBlocks + " blocks"); + + int blocks = getBigBlocksRequired(l); + + // First pad the data out so that it fits nicely into a whole number + // of blocks + if (l < SMALL_BLOCK_THRESHOLD) + { + requiredSize = SMALL_BLOCK_THRESHOLD; + } + else + { + requiredSize = blocks * BIG_BLOCK_SIZE; + } + + out = os; + + // logger.debug("smallBlockDepot requires " + numSmallBlockDepotBlocks + " big blocks"); + + // Do the calculations + excelDataBlocks = requiredSize/BIG_BLOCK_SIZE; + numBigBlockDepotBlocks = 1; + + int blockChainLength = (BIG_BLOCK_SIZE - BIG_BLOCK_DEPOT_BLOCKS_POS)/4; + + int startTotalBlocks = excelDataBlocks + + 8 + // summary block + 8 + // document information + additionalPropertyBlocks + + numRootEntryBlocks; + + int totalBlocks = startTotalBlocks + numBigBlockDepotBlocks; + + // Calculate the number of BBD blocks needed to hold this info + numBigBlockDepotBlocks = (int) Math.ceil( (double) totalBlocks / + (double) (BIG_BLOCK_SIZE/4)); + + // Does this affect the total? + totalBlocks = startTotalBlocks + numBigBlockDepotBlocks; + + // And recalculate + numBigBlockDepotBlocks = (int) Math.ceil( (double) totalBlocks / + (double) (BIG_BLOCK_SIZE/4)); + + // Does this affect the total? + totalBlocks = startTotalBlocks + numBigBlockDepotBlocks; + + // See if the excel bbd chain can fit into the header block. + // Remember to allow for the end of chain indicator + if (numBigBlockDepotBlocks > blockChainLength - 1 ) + { + // Sod it - we need an extension block. We have to go through + // the whole tiresome calculation again + extensionBlock = 0; + + // Compute the number of extension blocks + int bbdBlocksLeft = numBigBlockDepotBlocks - blockChainLength + 1; + + numExtensionBlocks = (int) Math.ceil((double) bbdBlocksLeft / + (double) (BIG_BLOCK_SIZE/4 - 1)); + + // Modify the total number of blocks required and recalculate the + // the number of bbd blocks + totalBlocks = startTotalBlocks + + numExtensionBlocks + + numBigBlockDepotBlocks; + numBigBlockDepotBlocks = (int) Math.ceil( (double) totalBlocks / + (double) (BIG_BLOCK_SIZE/4)); + + // The final total + totalBlocks = startTotalBlocks + + numExtensionBlocks + + numBigBlockDepotBlocks; + } + else + { + extensionBlock = -2; + numExtensionBlocks = 0; + } + + // Set the excel data start block to be after the header (and + // its extensions) + excelDataStartBlock = numExtensionBlocks; + + logger.debug("excelDataStartBlock " + excelDataStartBlock); + + // Set the bbd start block to be after all the excel data + bbdStartBlock = excelDataStartBlock + + excelDataBlocks + + additionalPropertyBlocks + + 16; + + logger.debug("bbdStartBlock " + bbdStartBlock); + + // Set the root start block to be after all the big block depot blocks + rootStartBlock = bbdStartBlock + + numBigBlockDepotBlocks; + + + if (totalBlocks != rootStartBlock + numRootEntryBlocks) + { + logger.warn("Root start block and total blocks are inconsistent " + + " generated file may be corrupt"); + logger.warn("RootStartBlock " + rootStartBlock + " totalBlocks " + totalBlocks); + } + } + + /** + * Reads the additional property sets from the read in compound file + * + * @return the number of blocks needed to store these property sets + */ + private void readAdditionalPropertySets + (jxl.read.biff.CompoundFile readCompoundFile) + throws CopyAdditionalPropertySetsException, IOException + { + if (readCompoundFile == null) + { + return; + } + + additionalPropertySets = new ArrayList(); + readPropertySets = new HashMap(); + + String[] psnames = readCompoundFile.getPropertySetNames(); + int blocksRequired = 0; + standardPropertySetMappings = new int[STANDARD_PROPERTY_SETS.length]; + + for (int i = 0 ; i < psnames.length ; i++) + { + // Add it to the hash map for later + PropertyStorage ps = readCompoundFile.getPropertySet(psnames[i]); + + // If the name is non standard, then retrieve the property set + // information + boolean standard = false; + for (int j = 0 ; j < STANDARD_PROPERTY_SETS.length && !standard ; j++) + { + if (psnames[i].equalsIgnoreCase(STANDARD_PROPERTY_SETS[j])) + { + standard = true; + ReadPropertyStorage rps = new ReadPropertyStorage(ps, null, i); + readPropertySets.put(psnames[i], rps); + } + } + + if (!standard) + { + try + { + byte[] data = null; + if (ps.size > 0 ) + { + data = readCompoundFile.getStream(ps.name); + } + else + { + data = new byte[0]; + } + ReadPropertyStorage rps = new ReadPropertyStorage(ps, data, i); + readPropertySets.put(psnames[i], rps); + additionalPropertySets.add(rps); + + int blocks = data.length >= SMALL_BLOCK_THRESHOLD ? + getBigBlocksRequired(data.length) : + SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + blocksRequired += blocks; + } + catch (BiffException e) + { + logger.error(e); + throw new CopyAdditionalPropertySetsException(); + } + } + } + + additionalPropertyBlocks = blocksRequired; + } + + + /** + * Writes out the excel file in OLE compound file format + * + * @exception IOException + */ + public void write() throws IOException + { + writeHeader(); + writeExcelData(); + writeDocumentSummaryData(); + writeSummaryData(); + writeAdditionalPropertySets(); + writeBigBlockDepot(); + writePropertySets(); + + // Don't flush or close the stream - this is handled by the enclosing File + // object + } + + /** + * Writes out any additional property sets + */ + private void writeAdditionalPropertySets() throws IOException + { + if (additionalPropertySets == null) + { + return; + } + + + /* + logger.debug("Writing property set " + rootEntryPropertySet.propertyStorage.name); + int numBlocks2 = rootEntryPropertySet.data.length >= SMALL_BLOCK_THRESHOLD ? + getBigBlocksRequired(rootEntryPropertySet.data.length) : + SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + int requiredSize2 = numBlocks2 * BIG_BLOCK_SIZE; + + out.write(rootEntryPropertySet.data, 0, rootEntryPropertySet.data.length); + + byte[] padding2 = new byte[requiredSize2 - rootEntryPropertySet.data.length]; + out.write(padding2, 0, padding2.length); + + logger.debug("data length " + rootEntryPropertySet.data.length + " Padding " + padding2.length); + */ + + for (Iterator i = additionalPropertySets.iterator(); i.hasNext() ;) + { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + byte[] data = rps.data; + + logger.debug("Writing property set " + rps.propertyStorage.name); + int numBlocks = data.length >= SMALL_BLOCK_THRESHOLD ? + getBigBlocksRequired(data.length) : + SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + int requiredSize = numBlocks * BIG_BLOCK_SIZE; + + out.write(data, 0, data.length); + + byte[] padding = new byte[requiredSize - data.length]; + out.write(padding, 0, padding.length); + } + } + + /** + * Writes out the excel data, padding it out with empty bytes as + * necessary + * Also write out empty + * + * @exception IOException + */ + private void writeExcelData() throws IOException + { + logger.debug("num excel data blocks " + excelDataBlocks + " excelData size " + requiredSize); + out.write(excelData, 0, size); + + byte[] padding = new byte[requiredSize - size]; + out.write(padding); + } + + /** + * Write out the document summary data. This is just blank + * + * @exception IOException + */ + private void writeDocumentSummaryData() throws IOException + { + byte[] padding = new byte[SMALL_BLOCK_THRESHOLD]; + + // Write out the summary information + out.write(padding); + } + + /** + * Write out the summary data. This is just blank + * + * @exception IOException + */ + private void writeSummaryData() throws IOException + { + byte[] padding = new byte[SMALL_BLOCK_THRESHOLD]; + + // Write out the summary information + out.write(padding); + } + + /** + * Writes the compound file header + * + * @exception IOException + */ + private void writeHeader() throws IOException + { + logger.debug("num extensions blocks for header: " + numExtensionBlocks); + // Build up the header array + byte[] headerBlock = new byte[BIG_BLOCK_SIZE]; + byte[] extensionBlockData = new byte[BIG_BLOCK_SIZE * numExtensionBlocks]; + + // Copy in the identifier + System.arraycopy(IDENTIFIER, 0, headerBlock, 0, IDENTIFIER.length); + + // Copy in some magic values - no idea what they mean + headerBlock[0x18] = 0x3e; + headerBlock[0x1a] = 0x3; + headerBlock[0x1c] = (byte) 0xfe; + headerBlock[0x1d] = (byte) 0xff; + headerBlock[0x1e] = 0x9; + headerBlock[0x20] = 0x6; + headerBlock[0x39] = 0x10; + + // Set the number of BBD blocks + IntegerHelper.getFourBytes(numBigBlockDepotBlocks, + headerBlock, + NUM_BIG_BLOCK_DEPOT_BLOCKS_POS); + + // Set the small block depot chain to -2 ie. no small block chain + IntegerHelper.getFourBytes(-2, + headerBlock, + SMALL_BLOCK_DEPOT_BLOCK_POS); + + // Set the extension block + IntegerHelper.getFourBytes(extensionBlock, + headerBlock, + EXTENSION_BLOCK_POS); + + // Set the number of extension blocks to be the number of BBD blocks - 1 + IntegerHelper.getFourBytes(numExtensionBlocks, + headerBlock, + NUM_EXTENSION_BLOCK_POS); + + // Set the root start block + IntegerHelper.getFourBytes(rootStartBlock, + headerBlock, + ROOT_START_BLOCK_POS); + + // Set the block numbers for the BBD. Set the BBD running + // after the excel data and summary information + int pos = BIG_BLOCK_DEPOT_BLOCKS_POS; + + // See how many blocks fit into the header + int blocksToWrite = Math.min(numBigBlockDepotBlocks, + (BIG_BLOCK_SIZE - + BIG_BLOCK_DEPOT_BLOCKS_POS)/4); + int extensionBlock = 0; + int blocksWritten = 0; + + for (int i = 0 ; i < blocksToWrite; i++) + { + IntegerHelper.getFourBytes(bbdStartBlock + i, + headerBlock, + pos); + pos += 4; + blocksWritten++; + } + + // Pad out the rest of the header with blanks + for (int i = pos; i < BIG_BLOCK_SIZE; i++) + { + headerBlock[i] = (byte) 0xff; + } + + out.write(headerBlock); + + // Write out the extension blocks + pos = 0; + + for (int extBlock = 0; extBlock < numExtensionBlocks; extBlock++) + { + blocksToWrite = Math.min(numBigBlockDepotBlocks - blocksWritten, + BIG_BLOCK_SIZE/4 -1); + + for(int j = 0 ; j < blocksToWrite; j++) + { + IntegerHelper.getFourBytes(bbdStartBlock + blocksWritten + j, + extensionBlockData, + pos); + pos += 4; + } + + blocksWritten += blocksToWrite; + + // Indicate the next block, or the termination of the chain + int nextBlock = (blocksWritten == numBigBlockDepotBlocks) ? + -2 : extBlock+1 ; + IntegerHelper.getFourBytes(nextBlock, extensionBlockData, pos); + pos +=4; + } + + if (numExtensionBlocks > 0) + { + // Pad out the rest of the extension block with blanks + for (int i = pos; i < extensionBlockData.length; i++) + { + extensionBlockData[i] = (byte) 0xff; + } + + out.write(extensionBlockData); + } + } + + /** + * Checks that the data can fit into the current BBD block. If not, + * then it moves on to the next block + * + * @exception IOException + */ + private void checkBbdPos() throws IOException + { + if (bbdPos >= BIG_BLOCK_SIZE) + { + // Write out the extension block. This will simply be the next block + out.write(bigBlockDepot); + + // Create a new block + bigBlockDepot = new byte[BIG_BLOCK_SIZE]; + bbdPos = 0; + } + } + + /** + * Writes out the big block chain + * + * @param startBlock the starting block of the big block chain + * @param numBlocks the number of blocks in the chain + * @exception IOException + */ + private void writeBlockChain(int startBlock, int numBlocks) + throws IOException + { + int blocksToWrite = numBlocks - 1; + int blockNumber = startBlock + 1; + + while (blocksToWrite > 0) + { + int bbdBlocks = Math.min(blocksToWrite, (BIG_BLOCK_SIZE - bbdPos)/4); + + for (int i = 0 ; i < bbdBlocks; i++) + { + IntegerHelper.getFourBytes(blockNumber, bigBlockDepot, bbdPos); + bbdPos +=4 ; + blockNumber++; + } + + blocksToWrite -= bbdBlocks; + checkBbdPos(); + } + + // Write the end of the block chain + IntegerHelper.getFourBytes(-2, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + } + + /** + * Writes the block chains for the additional property sets + * + * @exception IOException + */ + private void writeAdditionalPropertySetBlockChains() throws IOException + { + if (additionalPropertySets == null) + { + return; + } + + int blockNumber = excelDataStartBlock + excelDataBlocks + 16; + + /* + int numBlocks2 = rootEntryPropertySet.data.length >= SMALL_BLOCK_THRESHOLD ? + getBigBlocksRequired(rootEntryPropertySet.data.length) : + SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + + String psname2 = rootEntryPropertySet.propertyStorage.name; + logger.debug("writing big block chain for " + psname2 + " block " + blockNumber + " numBlocks " + numBlocks2); + writeBlockChain(blockNumber, numBlocks2); + blockNumber += numBlocks2; + */ + + for (Iterator i = additionalPropertySets.iterator(); i.hasNext() ; ) + { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + + int numBlocks = rps.data.length >= SMALL_BLOCK_THRESHOLD ? + getBigBlocksRequired(rps.data.length) : + SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + + String psname = rps.propertyStorage.name; + logger.debug("writing big block chain for " + psname + " block " + blockNumber + " numBlocks " + numBlocks); + writeBlockChain(blockNumber, numBlocks); + blockNumber += numBlocks; + } + } + + /** + * Writes out the Big Block Depot + * + * @exception IOException + */ + private void writeBigBlockDepot() throws IOException + { + // This is after the excel data, the summary information, the + // big block property sets and the small block depot + bigBlockDepot = new byte[BIG_BLOCK_SIZE]; + bbdPos = 0; + + // Write out the extension blocks, indicating them as special blocks + for (int i = 0 ; i < numExtensionBlocks; i++) + { + IntegerHelper.getFourBytes(-3, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + } + + writeBlockChain(excelDataStartBlock, excelDataBlocks); + + // The excel data has been written. Now write out the rest of it + + // Write the block chain for the summary information + int summaryInfoBlock = excelDataStartBlock + + excelDataBlocks + + additionalPropertyBlocks; + + for (int i = summaryInfoBlock; i < summaryInfoBlock + 7; i++) + { + IntegerHelper.getFourBytes(i + 1, bigBlockDepot, bbdPos); + bbdPos +=4 ; + checkBbdPos(); + } + + // Write the end of the block chain for the summary info block + IntegerHelper.getFourBytes(-2, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + + // Write the block chain for the document summary information + for (int i = summaryInfoBlock + 8; i < summaryInfoBlock + 15; i++) + { + IntegerHelper.getFourBytes(i + 1, bigBlockDepot, bbdPos); + bbdPos +=4 ; + checkBbdPos(); + } + + // Write the end of the block chain for the document summary + IntegerHelper.getFourBytes(-2, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + + // Write out the block chain for the copied property sets, if present + writeAdditionalPropertySetBlockChains(); + + // The Big Block Depot immediately follows the document summary. Denote + // these as a special block + for (int i = 0; i < numBigBlockDepotBlocks; i++) + { + IntegerHelper.getFourBytes(-3, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + } + + // Write the root entry + writeBlockChain(rootStartBlock, numRootEntryBlocks); + + // Pad out the remainder of the block + if (bbdPos != 0) + { + for (int i = bbdPos; i < BIG_BLOCK_SIZE; i++) + { + bigBlockDepot[i] = (byte) 0xff; + } + out.write(bigBlockDepot); + } + } + + /** + * Calculates the number of big blocks required to store data of the + * specified length + * + * @param length the length of the data + * @return the number of big blocks required to store the data + */ + private int getBigBlocksRequired(int length) + { + int blocks = length / BIG_BLOCK_SIZE; + + return (length % BIG_BLOCK_SIZE > 0 )? blocks + 1 : blocks; + } + + /** + * Calculates the number of small blocks required to store data of the + * specified length + * + * @param length the length of the data + * @return the number of small blocks required to store the data + */ + private int getSmallBlocksRequired(int length) + { + int blocks = length / SMALL_BLOCK_SIZE; + + return (length % SMALL_BLOCK_SIZE > 0 )? blocks + 1 : blocks; + } + + /** + * Writes out the property sets + * + * @exception IOException + */ + private void writePropertySets() throws IOException + { + byte[] propertySetStorage = new byte[BIG_BLOCK_SIZE * numRootEntryBlocks]; + + int pos = 0; + int[] mappings = null; + + // Build up the mappings array + if (additionalPropertySets != null) + { + mappings = new int[numPropertySets]; + + // Map the standard ones to the first four + for (int i = 0 ; i < STANDARD_PROPERTY_SETS.length ; i++) + { + ReadPropertyStorage rps = (ReadPropertyStorage) + readPropertySets.get(STANDARD_PROPERTY_SETS[i]); + mappings[rps.number] = i; + } + + // Now go through the original ones + int newMapping = STANDARD_PROPERTY_SETS.length; + for (Iterator i = additionalPropertySets.iterator(); i.hasNext(); ) + { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + mappings[rps.number] = newMapping; + newMapping++; + } + } + + int dir = 0; + int previous = 0; + int next = 0; + + // Set the root entry property set + PropertyStorage ps = new PropertyStorage(ROOT_ENTRY_NAME); + ps.setType(5); + ps.setStartBlock(-2); + ps.setSize(0); + ps.setPrevious(-1); + ps.setNext(-1); + ps.setColour(0); + + dir = 2; + if (additionalPropertySets != null) + { + ReadPropertyStorage rps = (ReadPropertyStorage) + readPropertySets.get(ROOT_ENTRY_NAME); + dir = mappings[rps.propertyStorage.directory]; + } + ps.setDirectory(dir); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + + // Set the workbook property set + ps = new PropertyStorage(WORKBOOK_NAME); + ps.setType(2); + ps.setStartBlock(excelDataStartBlock); + // start the excel data after immediately after this block + ps.setSize(requiredSize); + // alway use a big block stream - none of that messing around + // with small blocks + ps.setColour(1); + + previous = 3; + + if (additionalPropertySets != null) + { + ReadPropertyStorage rps = (ReadPropertyStorage) + readPropertySets.get(WORKBOOK_NAME); + previous = mappings[rps.propertyStorage.previous]; + } + + ps.setPrevious(previous); + ps.setNext(-1); + ps.setDirectory(-1); + // ps.setColour(1); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + // Set the summary information + ps = new PropertyStorage(SUMMARY_INFORMATION_NAME); + ps.setType(2); + ps.setStartBlock(excelDataStartBlock + excelDataBlocks); + ps.setSize(SMALL_BLOCK_THRESHOLD); + // ps.setColour(1); + + previous = 1; + next = 3; + + if (additionalPropertySets != null) + { + ReadPropertyStorage rps = (ReadPropertyStorage) + readPropertySets.get(SUMMARY_INFORMATION_NAME); + + previous = rps.propertyStorage.previous != - 1 ? + mappings[rps.propertyStorage.previous] : -1 ; + next = rps.propertyStorage.next != - 1 ? + mappings[rps.propertyStorage.next] : -1 ; + } + + ps.setPrevious(previous); + ps.setNext(next); + ps.setDirectory(-1); + // ps.setColour(1); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + // Set the document summary information + ps = new PropertyStorage(DOCUMENT_SUMMARY_INFORMATION_NAME); + ps.setType(2); + ps.setStartBlock(excelDataStartBlock + excelDataBlocks + 8); + ps.setSize(SMALL_BLOCK_THRESHOLD); + ps.setPrevious(-1); + ps.setNext(-1); + ps.setDirectory(-1); + // ps.setColour(1); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + + + // Write out the additional property sets + if (additionalPropertySets == null) + { + out.write(propertySetStorage); + return; + } + + int bigBlock = excelDataStartBlock + excelDataBlocks + 16 + + 18; + + for (Iterator i = additionalPropertySets.iterator() ; i.hasNext(); ) + { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + + ps = new PropertyStorage(rps.propertyStorage.name); + ps.setType(rps.propertyStorage.type); + ps.setStartBlock(bigBlock); + ps.setSize(Math.max(rps.propertyStorage.size, SMALL_BLOCK_THRESHOLD)); + + previous = rps.propertyStorage.previous != -1 ? + mappings[rps.propertyStorage.previous] : -1; + next = rps.propertyStorage.next != -1 ? + mappings[rps.propertyStorage.next] : -1; + dir = rps.propertyStorage.directory != -1 ? + mappings[rps.propertyStorage.directory] : -1; + + ps.setPrevious(previous); + ps.setNext(next); + ps.setDirectory(dir); + // ps.setColour(1); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + + if (rps.data.length >= SMALL_BLOCK_THRESHOLD) + { + bigBlock += getBigBlocksRequired(rps.data.length); + } + else + { + bigBlock += SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + } + } + + out.write(propertySetStorage); + } +} + + + + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/CopyAdditionalPropertySetsException.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/CopyAdditionalPropertySetsException.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/CopyAdditionalPropertySetsException.java 17 Aug 2012 14:51:09 -0000 1.1 @@ -0,0 +1,35 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +/** + * Exception thrown when attempting to copy a workbook which contains + * additional property sets + */ +public class CopyAdditionalPropertySetsException extends JxlWriteException +{ + /** + * Constructor + */ + public CopyAdditionalPropertySetsException() + { + super(copyPropertySets); + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/CountryRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/CountryRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/CountryRecord.java 17 Aug 2012 14:51:08 -0000 1.1 @@ -0,0 +1,84 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.CountryCode; +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + + +/** + * Record containing the localization information + */ +class CountryRecord extends WritableRecordData +{ + /** + * The user interface language + */ + private int language; + + /** + * The regional settings + */ + private int regionalSettings; + + /** + * Constructor + */ + public CountryRecord(CountryCode lang, CountryCode r) + { + super(Type.COUNTRY); + + language = lang.getValue(); + regionalSettings = r.getValue(); + } + + public CountryRecord(jxl.read.biff.CountryRecord cr) + { + super(Type.COUNTRY); + + language = cr.getLanguageCode(); + regionalSettings = cr.getRegionalSettingsCode(); + } + + /** + * Retrieves the data to be written to the binary file + * + * @return the binary data + */ + public byte[] getData() + { + byte[] data = new byte[4]; + + IntegerHelper.getTwoBytes(language, data, 0); + IntegerHelper.getTwoBytes(regionalSettings, data, 2); + + return data; + } +} + + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/DBCellRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/DBCellRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/DBCellRecord.java 17 Aug 2012 14:51:12 -0000 1.1 @@ -0,0 +1,124 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import java.util.ArrayList; +import java.util.Iterator; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Indexes the first row record of the block and each individual cell. + * This is invoked by the sheets write process + */ +class DBCellRecord extends WritableRecordData +{ + /** + * The file position of the first Row record in this block + */ + private int rowPos; + + /** + * The position of the start of the next cell after the first row. This + * is used as the offset for the cell positions + */ + private int cellOffset; + + /** + * The list of all cell positions in this block + */ + private ArrayList cellRowPositions; + + /** + * The position of this record in the file. Vital for calculating offsets + */ + private int position; + + /** + * Constructor + * + * @param rp the position of this row + */ + public DBCellRecord(int rp) + { + super(Type.DBCELL); + rowPos = rp; + cellRowPositions = new ArrayList(10); + } + + /** + * Sets the offset of this cell record within the sheet stream + * + * @param pos the offset + */ + void setCellOffset(int pos) + { + cellOffset = pos; + } + + /** + * Adds a cell + * + * @param pos + */ + void addCellRowPosition(int pos) + { + cellRowPositions.add(new Integer(pos)); + } + + /** + * Sets the position of this cell within the sheet stream + * + * @param pos the position + */ + void setPosition(int pos) + { + position = pos; + } + + /** + * Gets the binary data for this cell record + * + * @return the binary data + */ + protected byte[] getData() + { + byte[] data = new byte[4 + 2 * cellRowPositions.size()]; + + // Set the offset to the first row + IntegerHelper.getFourBytes(position - rowPos, data, 0); + + // Now add in all the cell offsets + int pos = 4; + int lastCellPos = cellOffset; + Iterator i = cellRowPositions.iterator(); + while (i.hasNext()) + { + int cellPos = ((Integer) i.next()).intValue(); + IntegerHelper.getTwoBytes(cellPos - lastCellPos, data, pos); + lastCellPos = cellPos; + pos += 2; + } + + return data; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/DSFRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/DSFRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/DSFRecord.java 17 Aug 2012 14:51:12 -0000 1.1 @@ -0,0 +1,57 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Stores a flag which indicates whether the file is a double stream + * file. For files written by JExcelAPI, this FALSE + */ +class DSFRecord extends WritableRecordData +{ + /** + * The data to be written to the binary file + */ + private byte[] data; + + /** + * Constructor + */ + public DSFRecord() + { + super(Type.DSF); + + // Hard code the fact that this is most assuredly not a double + // stream file + data = new byte[] {0,0}; + } + + /** + * The binary data to be written out + * + * @return the binary data + */ + public byte[] getData() + { + return data; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/DataValidation.java2 =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/DataValidation.java2,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/DataValidation.java2 17 Aug 2012 14:51:08 -0000 1.1 @@ -0,0 +1,213 @@ +/********************************************************************* +* +* Copyright (C) 2004 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; + +import common.Logger; + +import jxl.WorkbookSettings; + +/** + * Class which encapsulates a data validation (typically in the form of a + * dropdown list box + */ +public class DataValidation +{ + /** + * The logger + */ + private final static Logger logger = Logger.getLogger(DataValidation.class); + + /** + * The data validity list + */ + private DataValidityListRecord validityList; + + /** + * The data validity record + */ + private ArrayList validitySettings; + + /** + * The current position in the validitySettins array + */ + private int pos; + + /** + * Handle to the workbook + */ + private WritableWorkbookImpl workbook; + + /** + * Handle to the workbook settings + */ + private WorkbookSettings workbookSettings; + + /** + * Constructor + * + * @param dv the data validations from the read only sheet + */ + DataValidation(jxl.read.biff.DataValidation dv, + WritableWorkbookImpl w, + WorkbookSettings ws) + { + workbook = w; + workbookSettings = ws; + validityList = new DataValidityListRecord(dv.getDataValidityList()); + + jxl.read.biff.DataValiditySettingsRecord[] settings = + dv.getDataValiditySettings(); + + validitySettings = new ArrayList(settings.length); + for (int i = 0; i < settings.length ; i++) + { + validitySettings.add(new DataValiditySettingsRecord(settings[i], + workbook, + workbookSettings)); + } + } + + /** + * Constructor + * + * @param dv the data validations from the writable sheet + */ + DataValidation(DataValidation dv, + WritableWorkbookImpl w, + WorkbookSettings ws) + + { + workbook = w; + workbookSettings = ws; + validityList = new DataValidityListRecord(dv.validityList); + + validitySettings = new ArrayList(dv.validitySettings.size()); + + for (Iterator i = dv.validitySettings.iterator(); i.hasNext(); ) + { + DataValiditySettingsRecord dvsr = (DataValiditySettingsRecord) i.next(); + validitySettings.add + (new DataValiditySettingsRecord(dvsr, + workbook, + workbookSettings)); + } + } + + + /** + * Writes out the data validation + * + * @exception IOException + * @param outputFile the output file + */ + public void write(File outputFile) throws IOException + { + if (!validityList.hasDVRecords()) + { + return; + } + + outputFile.write(validityList); + + for (Iterator i = validitySettings.iterator(); i.hasNext() ; ) + { + DataValiditySettingsRecord dv = (DataValiditySettingsRecord) i.next(); + outputFile.write(dv); + } + } + + /** + * Inserts a row + * + * @param row the inserted row + */ + public void insertRow(int row) + { + for (Iterator i = validitySettings.iterator(); i.hasNext() ; ) + { + DataValiditySettingsRecord dv = (DataValiditySettingsRecord) i.next(); + dv.insertRow(row); + } + } + + /** + * Inserts a row + * + * @param row the inserted row + */ + public void removeRow(int row) + { + for (Iterator i = validitySettings.iterator(); i.hasNext() ; ) + { + DataValiditySettingsRecord dv = (DataValiditySettingsRecord) i.next(); + + if (dv.getFirstRow() == row && dv.getLastRow() == row) + { + i.remove(); + validityList.dvRemoved(); + } + else + { + dv.removeRow(row); + } + } + } + + /** + * Inserts a column + * + * @param col the inserted column + */ + public void insertColumn(int col) + { + for (Iterator i = validitySettings.iterator(); i.hasNext() ; ) + { + DataValiditySettingsRecord dv = (DataValiditySettingsRecord) i.next(); + dv.insertColumn(col); + } + } + + /** + * Removes a column + * + * @param col the inserted column + */ + public void removeColumn(int col) + { + for (Iterator i = validitySettings.iterator(); i.hasNext() ; ) + { + DataValiditySettingsRecord dv = (DataValiditySettingsRecord) i.next(); + + if (dv.getFirstColumn() == col && dv.getLastColumn() == col) + { + i.remove(); + validityList.dvRemoved(); + } + else + { + dv.removeColumn(col); + } + } + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/DataValidityListRecord.java2 =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/DataValidityListRecord.java2,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/DataValidityListRecord.java2 17 Aug 2012 14:51:11 -0000 1.1 @@ -0,0 +1,111 @@ +/********************************************************************* +* +* Copyright (C) 2004 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.DValParser; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + + +/** + * Data validity settings + */ +class DataValidityListRecord extends WritableRecordData +{ + /** + * The binary data + */ + private byte[] data; + + /** + * The dval parser + */ + private DValParser dvalParser; + + /** + * Constructor + * + * @param dvlr the record copied from a read only sheet + */ + DataValidityListRecord(jxl.read.biff.DataValidityListRecord dvlr) + { + super(Type.DVAL); + + data = dvlr.getData(); + } + + /** + * Constructor + * + * @param dvlr the record copied from a writable sheet + */ + DataValidityListRecord(DataValidityListRecord dvlr) + { + super(Type.DVAL); + + data = new byte[dvlr.data.length]; + System.arraycopy(dvlr.data, 0, data, 0, data.length); + } + + /** + * Retrieves the data for output to binary file + * + * @return the data to be written + */ + public byte[] getData() + { + if (dvalParser == null) + { + return data; + } + + return dvalParser.getData(); + } + + /** + * Called when a remove row or column results in one of DV records being + * removed + */ + void dvRemoved() + { + if (dvalParser == null) + { + dvalParser = new DValParser(data); + } + + dvalParser.dvRemoved(); + } + + /** + * Accessor for the number of DV records + * + * @return the number of DV records for this list + */ + public boolean hasDVRecords() + { + if (dvalParser == null) + { + return true; + } + + return dvalParser.getNumberOfDVRecords() > 0; + } + +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/DataValiditySettingsRecord.java2 =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/DataValiditySettingsRecord.java2,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/DataValiditySettingsRecord.java2 17 Aug 2012 14:51:11 -0000 1.1 @@ -0,0 +1,247 @@ +/********************************************************************* +* +* Copyright (C) 2004 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import common.Logger; + +import jxl.WorkbookSettings; +import jxl.biff.DVParser; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; +import jxl.biff.formula.FormulaException; + +/** + * Data validity settings + */ +class DataValiditySettingsRecord extends WritableRecordData +{ + /** + * The logger + */ + private static final Logger logger = + Logger.getLogger(DataValiditySettingsRecord.class); + + /** + * The binary data + */ + private byte[] data; + + /** + * The reader + */ + private DVParser dvParser; + + /** + * Handle to the workbook + */ + private WritableWorkbookImpl workbook; + + /** + * Handle to the workbook settings + */ + private WorkbookSettings workbookSettings; + + /** + * Constructor + * + * @param dvsr the record copied from a read only sheet + */ + DataValiditySettingsRecord(jxl.read.biff.DataValiditySettingsRecord dvsr, + WritableWorkbookImpl w, + WorkbookSettings ws) + { + super(Type.DV); + workbook = w; + workbookSettings = ws; + + + data = dvsr.getData(); + } + + /** + * Constructor + * + * @param dvsr the record copied from a writable sheet + */ + DataValiditySettingsRecord(DataValiditySettingsRecord dvsr, + WritableWorkbookImpl w, + WorkbookSettings ws) + { + super(Type.DV); + workbook = w; + workbookSettings = ws; + + data = new byte[dvsr.data.length]; + System.arraycopy(dvsr.data, 0, data, 0, data.length); + } + + /** + * Initializes the dvParser + */ + private void initialize() + { + try + { + if (dvParser == null) + { + dvParser = new DVParser(data, workbook, workbook, workbookSettings); + } + } + catch (FormulaException e) + { + logger.warn("Cannot read drop down range " + e.getMessage()); + e.printStackTrace(); + } + } + /** + * Retrieves the data for output to binary file + * + * @return the data to be written + */ + public byte[] getData() + { + if (dvParser == null) + { + return data; + } + + return dvParser.getData(); + } + + /** + * Inserts a row + * + * @param row the row to insert + */ + public void insertRow(int row) + { + if (dvParser == null) + { + initialize(); + } + + dvParser.insertRow(row); + } + + /** + * Removes a row + * + * @param row the row to insert + */ + public void removeRow(int row) + { + if (dvParser == null) + { + initialize(); + } + + dvParser.removeRow(row); + } + + /** + * Inserts a row + * + * @param col the row to insert + */ + public void insertColumn(int col) + { + if (dvParser == null) + { + initialize(); + } + + dvParser.insertColumn(col); + } + + /** + * Removes a column + * + * @param col the row to insert + */ + public void removeColumn(int col) + { + if (dvParser == null) + { + initialize(); + } + + dvParser.removeColumn(col); + } + + /** + * Accessor for first column + * + * @return the first column + */ + public int getFirstColumn() + { + if (dvParser == null) + { + initialize(); + } + + return dvParser.getFirstColumn(); + } + + /** + * Accessor for the last column + * + * @return the last column + */ + public int getLastColumn() + { + if (dvParser == null) + { + initialize(); + } + + return dvParser.getLastColumn(); + } + + /** + * Accessor for first row + * + * @return the first row + */ + public int getFirstRow() + { + if (dvParser == null) + { + initialize(); + } + + return dvParser.getFirstRow(); + } + + /** + * Accessor for the last row + * + * @return the last row + */ + public int getLastRow() + { + if (dvParser == null) + { + initialize(); + } + + return dvParser.getLastRow(); + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/DateFormatRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/DateFormatRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/DateFormatRecord.java 17 Aug 2012 14:51:12 -0000 1.1 @@ -0,0 +1,49 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.FormatRecord; + +/** + * A class which contains a date format + */ +public class DateFormatRecord extends FormatRecord +{ + /** + * Constructor + * + * @param fmt the date format + */ + protected DateFormatRecord(String fmt) + { + super(); + + // Do the replacements in the format string + String fs = fmt; + + fs = replace(fs, "a", "AM/PM"); + fs = replace(fs, "S", "0"); + + setFormatString(fs); + } +} + + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/DateRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/DateRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/DateRecord.java 17 Aug 2012 14:51:08 -0000 1.1 @@ -0,0 +1,343 @@ +/********************************************************************* +* +* Copyright (C) 2001 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import java.text.DateFormat; +import java.util.Calendar; +import java.util.Date; + +import common.Logger; + +import jxl.CellType; +import jxl.DateCell; +import jxl.biff.DoubleHelper; +import jxl.biff.Type; +import jxl.format.CellFormat; +import jxl.write.DateFormats; +import jxl.write.WritableCellFormat; + +/** + * A date stored in the database + */ +public abstract class DateRecord extends CellValue +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(DateRecord.class); + + /** + * The excel value of the date + */ + private double value; + /** + * The java representation of the date + */ + private Date date; + + /** + * Indicates whether this is a full date, or just a time only + */ + private boolean time; + + // The number of days between 01 Jan 1900 and 01 Jan 1970 - this gives + // the UTC offset + /** + */ + private final static int utcOffsetDays = 25569; + + // The number of milliseconds in a day + /** + */ + private final static long msInADay = 24 * 60 * 60 * 1000; + + /** + * This is package protected so that the worksheet might detect + * whether or not to override it with the column cell format + */ + static final WritableCellFormat defaultDateFormat = + new WritableCellFormat(DateFormats.DEFAULT); + + // The number of days between 1 Jan 1900 and 1 March 1900. Excel thinks + // the day before this was 29th Feb 1900, but it was 28th Feb 19000. + // I guess the programmers thought nobody would notice that they + // couldn't be bothered to program this dating anomaly properly + /** + */ + private final static int nonLeapDay = 61; + + /** + * Class definition for a dummy variable + */ + protected static final class GMTDate + { + public GMTDate(){} + }; + + /** + * Constructor invoked by the user API + * + * @param c the column + * @param r the row + * @param d the date + */ + protected DateRecord(int c, int r, Date d) + { + this(c, r, d, defaultDateFormat, false); + } + + /** + * Constructor invoked by the user API + * + * @param c the column + * @param r the row + * @param d the date + * @param a adjust timezone + */ + protected DateRecord(int c, int r, Date d, GMTDate a) + { + this(c, r, d, defaultDateFormat, false); + } + + /** + * Constructor invoked from the user API + * + * @param c the column + * @param r the row + * @param st the format for the date + * @param d the date + */ + protected DateRecord(int c, int r, Date d, CellFormat st) + { + super(Type.NUMBER, c, r,st); + date = d; + calculateValue(true); + } + + /** + * Constructor invoked from the user API + * + * @param c the column + * @param r the row + * @param st the format for the date + * @param d the date + * @param a adjust for the timezone + */ + protected DateRecord(int c, int r, Date d, CellFormat st, GMTDate a) + { + super(Type.NUMBER, c, r, st); + date = d; + calculateValue(false); + } + + /** + * Constructor invoked from the API + * + * @param c the column + * @param r the row + * @param st the date format + * @param tim time indicator + * @param d the date + */ + protected DateRecord(int c, int r, Date d, CellFormat st, boolean tim) + { + super(Type.NUMBER, c, r, st); + date = d; + time = tim; + calculateValue(false); + } + + /** + * Constructor invoked when copying a readable spreadsheet + * + * @param dc the date to copy + */ + protected DateRecord(DateCell dc) + { + super(Type.NUMBER, dc); + date = dc.getDate(); + time = dc.isTime(); + calculateValue(false); + } + + /** + * Copy constructor + * + * @param c the column + * @param r the row + * @param dr the record to copy + */ + protected DateRecord(int c, int r, DateRecord dr) + { + super(Type.NUMBER, c, r, dr); + value = dr.value; + time = dr.time; + date = dr.date; + } + + /** + * Calculates the 1900 based numerical value based upon the utc value held + * in the date object + * + * @param adjust TRUE if we want to incorporate timezone information + * into the raw UTC date eg. when copying from a spreadsheet + */ + private void calculateValue(boolean adjust) + { + // Offsets for current time zone + long zoneOffset = 0; + long dstOffset = 0; + + // Get the timezone and dst offsets if we want to take these into + // account + if (adjust) + { + // Get the current calender, replete with timezone information + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + + zoneOffset = cal.get(Calendar.ZONE_OFFSET); + dstOffset = cal.get(Calendar.DST_OFFSET); + } + + long utcValue = date.getTime() + zoneOffset + dstOffset; + + // Convert this to the number of days, plus fractions of a day since + // 01 Jan 1970 + double utcDays = (double) utcValue / (double) msInADay; + + // Add in the offset to get the number of days since 01 Jan 1900 + value = utcDays + utcOffsetDays; + + // Work round a bug in excel. Excel seems to think there is a date + // called the 29th Feb, 1900 - but this was not a leap year. + // Therefore for values less than 61, we must subtract 1. Only do + // this for full dates, not times + if (!time && value < nonLeapDay) + { + value -= 1; + } + + // If this refers to a time, then get rid of the integer part + if (time) + { + value = value - (int) value; + } + } + + /** + * Returns the content type of this cell + * + * @return the content type for this cell + */ + public CellType getType() + { + return CellType.DATE; + } + + /** + * Gets the binary data for writing + * + * @return the binary data + */ + public byte[] getData() + { + byte[] celldata = super.getData(); + byte[] data = new byte[celldata.length + 8]; + System.arraycopy(celldata, 0, data, 0, celldata.length); + DoubleHelper.getIEEEBytes(value, data, celldata.length); + + return data; + } + + /** + * Quick and dirty function to return the contents of this cell as a string. + * For more complex manipulation of the contents, it is necessary to cast + * this interface to correct subinterface + * + * @return the contents of this cell as a string + */ + public String getContents() + { + return date.toString(); + } + + /** + * Sets the date in this cell + * + * @param d the date + */ + protected void setDate(Date d) + { + date = d; + calculateValue(true); + } + + /** + * Sets the date in this cell, taking the timezone into account + * + * @param d the date + * @param a adjust for timezone + */ + protected void setDate(Date d, GMTDate a) + { + date = d; + calculateValue(false); + } + + + /** + * Gets the date contained in this cell + * + * @return the cell contents + */ + public Date getDate() + { + return date; + } + + /** + * Indicates whether the date value contained in this cell refers to a date, + * or merely a time. When writing a cell, all dates are fully defined, + * even if they refer to a time + * + * @return FALSE if this is full date, TRUE if a time + */ + public boolean isTime() + { + return time; + } + + /** + * Gets the DateFormat used to format the cell. This will normally be + * the format specified in the excel spreadsheet, but in the event of any + * difficulty parsing this, it will revert to the default date/time format. + * + * @return the DateFormat object used to format the date in the original + * excel cell + */ + public DateFormat getDateFormat() + { + return null; + } +} + + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/DefaultColumnWidth.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/DefaultColumnWidth.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/DefaultColumnWidth.java 17 Aug 2012 14:51:08 -0000 1.1 @@ -0,0 +1,62 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * The default column width for a workbook + */ +class DefaultColumnWidth extends WritableRecordData +{ + /** + * The default column width + */ + private int width; + /** + * The binary data + */ + private byte[] data; + + /** + * Constructor + * + * @param w the default column width + */ + public DefaultColumnWidth(int w) + { + super(Type.DEFCOLWIDTH); + width = w; + data = new byte[2]; + IntegerHelper.getTwoBytes(width, data, 0); + } + + /** + * Gets the binary data for writing to the stream + * + * @return the binary data + */ + protected byte[] getData() + { + return data; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/DefaultRowHeightRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/DefaultRowHeightRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/DefaultRowHeightRecord.java 17 Aug 2012 14:51:12 -0000 1.1 @@ -0,0 +1,77 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * The default row height for cells in the workbook + */ +class DefaultRowHeightRecord extends WritableRecordData +{ + /** + * The binary data + */ + private byte[] data; + /** + * The default row height + */ + private int rowHeight; + + /** + * Indicates whether or not the default row height has been changed + */ + private boolean changed; + + /** + * Constructor + * + * @param height the default row height + * @param ch TRUE if the default value has been changed, false + * otherwise + */ + public DefaultRowHeightRecord(int h, boolean ch) + { + super(Type.DEFAULTROWHEIGHT); + data = new byte[4]; + rowHeight = h; + changed = ch; + } + + /** + * Gets the binary data for writing to the output stream + * + * @return the binary data + */ + public byte[] getData() + { + if (changed) + { + data[0] |= 0x1; + } + + IntegerHelper.getTwoBytes(rowHeight, data, 2); + return data; + } +} + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/DeltaRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/DeltaRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/DeltaRecord.java 17 Aug 2012 14:51:12 -0000 1.1 @@ -0,0 +1,79 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.DoubleHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which stores the maximum change value from the Options + * dialog + */ +class DeltaRecord extends WritableRecordData +{ + /** + * The binary data + */ + private byte[] data; + /** + * The number of iterations + */ + private double iterationValue; + + /** + * Constructor + * + * @param itval + */ + public DeltaRecord(double itval) + { + super(Type.DELTA); + iterationValue = itval; + + data = new byte[8]; + } + + + /** + * Gets the binary data for writing to the output file + * + * @return the binary data + */ + public byte[] getData() + { + DoubleHelper.getIEEEBytes(iterationValue, data, 0); + + /* long val = Double.doubleToLongBits(iterationValue); + data[0] = (byte) (val & 0xff); + data[1] = (byte) ((val & 0xff00) >> 8); + data[2] = (byte) ((val & 0xff0000) >> 16); + data[3] = (byte) ((val & 0xff000000) >> 24); + data[4] = (byte) ((val & 0xff00000000L) >> 32); + data[5] = (byte) ((val & 0xff0000000000L) >> 40); + data[6] = (byte) ((val & 0xff000000000000L) >> 48); + data[7] = (byte) ((val & 0xff00000000000000L) >> 56) ; + */ + + return data; + } +} + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/DimensionRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/DimensionRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/DimensionRecord.java 17 Aug 2012 14:51:08 -0000 1.1 @@ -0,0 +1,73 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which contains the bounds of the sheet + */ +class DimensionRecord extends WritableRecordData +{ + /** + * The number of rows in the sheet + */ + private int numRows; + /** + * The number of columns in the sheet + */ + private int numCols; + + /** + * The binary data + */ + private byte[] data; + + /** + * Constructor + * + * @param c the number of columns + * @param r the number of rows + */ + public DimensionRecord(int r, int c) + { + super(Type.DIMENSION); + numRows = r; + numCols = c; + + data = new byte[14]; + + IntegerHelper.getFourBytes(numRows, data, 4); + IntegerHelper.getTwoBytes(numCols, data, 10); + } + + /** + * Gets the binary data to be written to the output file + * + * @return the binary data + */ + protected byte[] getData() + { + return data; + } + +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/EOFRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/EOFRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/EOFRecord.java 17 Aug 2012 14:51:12 -0000 1.1 @@ -0,0 +1,48 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which marks the end of the current stream + */ +class EOFRecord extends WritableRecordData +{ + /** + * Constructor + */ + public EOFRecord() + { + super(Type.EOF); + } + + /** + * Returns the binary data to be written to the output file + * + * @return the binary data + */ + public byte[] getData() + { + return new byte[0]; + } +} + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/ExcelDataOutput.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/ExcelDataOutput.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/ExcelDataOutput.java 17 Aug 2012 14:51:12 -0000 1.1 @@ -0,0 +1,62 @@ +/********************************************************************* +* +* Copyright (C) 2007 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import java.io.OutputStream; +import java.io.IOException; + +/** + * Interface to abstract away an in-memory output or a temporary file + * output. Used by the File object + */ +interface ExcelDataOutput +{ + /** + * Appends the bytes to the end of the output + * + * @param d the data to write to the end of the array + */ + public void write(byte[] bytes) throws IOException; + + /** + * Gets the current position within the file + * + * @return the position within the file + */ + public int getPosition() throws IOException; + + /** + * Sets the data at the specified position to the contents of the array + * + * @param pos the position to alter + * @param newdata the data to modify + */ + public void setData(byte[] newdata, int pos) throws IOException; + + /** + * Writes the data to the output stream + */ + public void writeData(OutputStream out) throws IOException; + + /** + * Called when the final compound file has been written + */ + public void close() throws IOException; +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/ExtendedSSTRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/ExtendedSSTRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/ExtendedSSTRecord.java 17 Aug 2012 14:51:12 -0000 1.1 @@ -0,0 +1,115 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Indicates an extension to the Shared String Table. Currently this + * contains blank records + * + * Thanks to Guenther for contributing a proper implementation of the EXTSST + * record, replacing my previous dummy version + */ +class ExtendedSSTRecord extends WritableRecordData +{ + private static final int infoRecordSize = 8; + private int numberOfStrings; + private int[] absoluteStreamPositions; + private int[] relativeStreamPositions; + private int currentStringIndex = 0; + + /** + * Constructor + * + * @param numstrings the number of strings per bucket + * @param streampos the absolute stream position of the beginning of + * the SST record + */ + public ExtendedSSTRecord(int newNumberOfStrings) + { + super(Type.EXTSST); + numberOfStrings = newNumberOfStrings; + int numberOfBuckets = getNumberOfBuckets(); + absoluteStreamPositions = new int[numberOfBuckets]; + relativeStreamPositions = new int[numberOfBuckets]; + currentStringIndex = 0; + } + + public int getNumberOfBuckets() + { + int numberOfStringsPerBucket = getNumberOfStringsPerBucket(); + return numberOfStringsPerBucket != 0 ? + (numberOfStrings + numberOfStringsPerBucket - 1) / + numberOfStringsPerBucket : 0; + } + + public int getNumberOfStringsPerBucket() + { + // XXX + // should come up with a more clever calculation + // bucket limit should not be bigger than 1024, otherwise we end + // up with too many buckets and would have to write continue records + // for the EXTSST record which we want to avoid for now. + final int bucketLimit = 128; + return (numberOfStrings + bucketLimit - 1) / bucketLimit; + } + + public void addString(int absoluteStreamPosition, + int relativeStreamPosition) + { + absoluteStreamPositions[currentStringIndex] = + absoluteStreamPosition + relativeStreamPosition; + relativeStreamPositions[currentStringIndex] = relativeStreamPosition; + currentStringIndex++; + } + + /** + * Gets the binary data to be written out + * + * @return the binary data + */ + public byte[] getData() + { + int numberOfBuckets = getNumberOfBuckets(); + byte[] data = new byte[2 + (8 * numberOfBuckets)]; + // number of strings per bucket + IntegerHelper.getTwoBytes(getNumberOfStringsPerBucket(), data, 0); + + for (int i = 0; i < numberOfBuckets; i++) + { + // absolute stream position + IntegerHelper.getFourBytes(absoluteStreamPositions[i], + data, + 2 + (i * infoRecordSize)); + // relative offset + IntegerHelper.getTwoBytes(relativeStreamPositions[i], + data, + 6 + (i * infoRecordSize)); + // reserved + // IntegerHelper.getTwoBytes(0x0, data, 8 + (i * infoRecordSize)); + } + + return data; + } +} + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/ExternalNameRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/ExternalNameRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/ExternalNameRecord.java 17 Aug 2012 14:51:12 -0000 1.1 @@ -0,0 +1,74 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import common.Logger; + +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * An external sheet record, used to maintain integrity when formulas + * are copied from read databases + */ +class ExternalNameRecord extends WritableRecordData +{ + /** + * The logger + */ + Logger logger = Logger.getLogger(ExternalNameRecord.class); + + /** + * The name of the addin + */ + private String name; + + /** + * Constructor used for writable workbooks + */ + public ExternalNameRecord(String n) + { + super(Type.EXTERNNAME); + name = n; + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + byte[] data = new byte[name.length() * 2 + 12]; + + data[6] = (byte) name.length(); + data[7] = 0x1; + StringHelper.getUnicodeBytes(name, data, 8); + + int pos = 8 + name.length() * 2; + data[pos] = 0x2; + data[pos+1] = 0x0; + data[pos+2] = 0x1c; + data[pos+3] = 0x17; + + return data; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/ExternalSheetRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/ExternalSheetRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/ExternalSheetRecord.java 17 Aug 2012 14:51:12 -0000 1.1 @@ -0,0 +1,252 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import java.util.ArrayList; +import java.util.Iterator; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * An external sheet record, used to maintain integrity when formulas + * are copied from read databases + */ +class ExternalSheetRecord extends WritableRecordData +{ + /** + * The underlying external sheet data + */ + private byte[] data; + + /** + * The list of XTI structures + */ + private ArrayList xtis; + + /** + * An XTI structure + */ + private static class XTI + { + int supbookIndex; + int firstTab; + int lastTab; + + XTI(int s, int f, int l) + { + supbookIndex = s; + firstTab = f; + lastTab = l; + } + + void sheetInserted(int index) + { + if (firstTab >= index) + { + firstTab++; + } + + if (lastTab >= index) + { + lastTab++; + } + } + + void sheetRemoved(int index) + { + if (firstTab == index) + { + firstTab = 0; + } + + if (lastTab == index) + { + lastTab = 0; + } + + if (firstTab > index) + { + firstTab--; + } + + if (lastTab > index) + { + lastTab--; + } + } + } + + /** + * Constructor + * + * @param esf the external sheet record to copy + */ + public ExternalSheetRecord(jxl.read.biff.ExternalSheetRecord esf) + { + super(Type.EXTERNSHEET); + + xtis = new ArrayList(esf.getNumRecords()); + XTI xti = null; + for (int i = 0 ; i < esf.getNumRecords(); i++) + { + xti = new XTI(esf.getSupbookIndex(i), + esf.getFirstTabIndex(i), + esf.getLastTabIndex(i)); + xtis.add(xti); + } + } + + /** + * Constructor used for writable workbooks + */ + public ExternalSheetRecord() + { + super(Type.EXTERNSHEET); + xtis = new ArrayList(); + } + + /** + * Gets the extern sheet index for the specified parameters, creating + * a new xti record if necessary + * @param supbookind the internal supbook reference + * @param sheetind the sheet index + */ + int getIndex(int supbookind, int sheetind) + { + Iterator i = xtis.iterator(); + XTI xti = null; + boolean found = false; + int pos = 0; + while (i.hasNext() && !found) + { + xti = (XTI) i.next(); + + if (xti.supbookIndex == supbookind && + xti.firstTab == sheetind) + { + found = true; + } + else + { + pos++; + } + } + + if (!found) + { + xti = new XTI(supbookind, sheetind, sheetind); + xtis.add(xti); + pos = xtis.size() - 1; + } + + return pos; + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + byte[] data = new byte[2 + xtis.size() * 6]; + + int pos = 0; + IntegerHelper.getTwoBytes(xtis.size(), data, 0); + pos += 2; + + Iterator i = xtis.iterator(); + XTI xti = null; + while (i.hasNext()) + { + xti = (XTI) i.next(); + IntegerHelper.getTwoBytes(xti.supbookIndex, data, pos); + IntegerHelper.getTwoBytes(xti.firstTab, data, pos+2); + IntegerHelper.getTwoBytes(xti.lastTab, data, pos+4); + pos +=6 ; + } + + return data; + } + + /** + * Gets the supbook index for the specified external sheet + * + * @param the index of the supbook record + * @return the supbook index + */ + public int getSupbookIndex(int index) + { + return ((XTI) xtis.get(index)).supbookIndex; + } + + /** + * Gets the first tab index for the specified external sheet + * + * @param the index of the supbook record + * @return the first tab index + */ + public int getFirstTabIndex(int index) + { + return ((XTI) xtis.get(index)).firstTab; + } + + /** + * Gets the last tab index for the specified external sheet + * + * @param the index of the supbook record + * @return the last tab index + */ + public int getLastTabIndex(int index) + { + return ((XTI) xtis.get(index)).lastTab; + } + + /** + * Called when a sheet has been inserted via the API + * @param the position of the insertion + */ + void sheetInserted(int index) + { + XTI xti = null; + for (Iterator i = xtis.iterator(); i.hasNext() ; ) + { + xti = (XTI) i.next(); + xti.sheetInserted(index); + } + } + + /** + * Called when a sheet has been removed via the API + * @param the position of the insertion + */ + void sheetRemoved(int index) + { + XTI xti = null; + for (Iterator i = xtis.iterator(); i.hasNext() ; ) + { + xti = (XTI) i.next(); + xti.sheetRemoved(index); + } + } + +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/File.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/File.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/File.java 17 Aug 2012 14:51:12 -0000 1.1 @@ -0,0 +1,191 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import java.io.IOException; +import java.io.OutputStream; + +import common.Logger; + +import jxl.WorkbookSettings; +import jxl.biff.ByteData; + +/** + * A file of excel data to be written out. All the excel data is held + * in memory, and when the close method is called a CompoundFile object + * is used to write the Biff oriented excel data in the CompoundFile + * format + */ +public final class File +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(File.class); + + /** + * The data from the excel 97 file + */ + private ExcelDataOutput data; + /** + * The current position within the file + */ + private int pos; + /** + * The output stream + */ + private OutputStream outputStream; + /** + * The initial file size + */ + private int initialFileSize; + /** + * The amount to increase the growable array by + */ + private int arrayGrowSize; + /** + * The workbook settings + */ + private WorkbookSettings workbookSettings; + /** + * The read compound file. This will only be non-null if there are macros + * or other property sets of that ilk which that we should be copying + */ + jxl.read.biff.CompoundFile readCompoundFile; + + /** + * Constructor + * + * @param os the output stream + * @param ws the configuration settings for this workbook + * @param rcf the rea compound file + */ + File(OutputStream os, WorkbookSettings ws, jxl.read.biff.CompoundFile rcf) + throws IOException + { + outputStream = os; + workbookSettings = ws; + readCompoundFile = rcf; + createDataOutput(); + } + + private void createDataOutput() throws IOException + { + if (workbookSettings.getUseTemporaryFileDuringWrite()) + { + data = new FileDataOutput + (workbookSettings.getTemporaryFileDuringWriteDirectory()); + } + else + { + initialFileSize = workbookSettings.getInitialFileSize(); + arrayGrowSize = workbookSettings.getArrayGrowSize(); + + data = new MemoryDataOutput(initialFileSize, arrayGrowSize); + } + } + + /** + * Closes the file. In fact, this writes out all the excel data + * to disk using a CompoundFile object, and then frees up all the memory + * allocated to the workbook + * + * @exception IOException + * @param cs TRUE if this should close the stream, FALSE if the application + * closes it + */ + void close(boolean cs) throws IOException, JxlWriteException + { + CompoundFile cf = new CompoundFile(data, + data.getPosition(), + outputStream, + readCompoundFile); + cf.write(); + + outputStream.flush(); + data.close(); + + if (cs) + { + outputStream.close(); + } + + // Cleanup the memory a bit + data = null; + + if (!workbookSettings.getGCDisabled()) + { + System.gc(); + } + } + + /** + * Adds the biff record data to the memory allocated for this File + * + * @exception IOException + * @param record the record to add to the excel data + */ + public void write(ByteData record) throws IOException + { + byte[] bytes = record.getBytes(); + + data.write(bytes); + } + + /** + * Gets the current position within the file + * + * @return the current position + */ + int getPos() throws IOException + { + return data.getPosition(); + } + + /** + * Used to manually alter the contents of the written out data. This + * is used when cross-referencing cell records + * + * @param pos the position to alter + * @param newdata the data to modify + */ + void setData(byte[] newdata, int pos) throws IOException + { + data.setData(newdata, pos); + } + + /** + * Sets a new output file. This allows the same workbook to be + * written to various different output files without having to + * read in any templates again + * + * @param os the output stream + */ + public void setOutputFile(OutputStream os) throws IOException + { + if (data != null) + { + logger.warn("Rewriting a workbook with non-empty data"); + } + + outputStream = os; + createDataOutput(); + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/FileDataOutput.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/FileDataOutput.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/FileDataOutput.java 17 Aug 2012 14:51:13 -0000 1.1 @@ -0,0 +1,123 @@ +/********************************************************************* +* +* Copyright (C) 2007 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import java.io.OutputStream; +import java.io.IOException; +import java.io.File; +import java.io.RandomAccessFile; + +import common.Logger; + +/** + * Used to generate the excel biff data using a temporary file. This + * class wraps a RandomAccessFile + */ +class FileDataOutput implements ExcelDataOutput +{ + // The logger + private static Logger logger = Logger.getLogger(FileDataOutput.class); + + /** + * The temporary file + */ + private File temporaryFile; + + /** + * The excel data + */ + private RandomAccessFile data; + + /** + * Constructor + * + * @param tmpdir the temporary directory used to write files. If this is + * NULL then the sytem temporary directory will be used + */ + public FileDataOutput(File tmpdir) throws IOException + { + temporaryFile = File.createTempFile("jxl",".tmp", tmpdir); + temporaryFile.deleteOnExit(); + data = new RandomAccessFile(temporaryFile, "rw"); + } + + /** + * Writes the bytes to the end of the array, growing the array + * as needs dictate + * + * @param d the data to write to the end of the array + */ + public void write(byte[] bytes) throws IOException + { + data.write(bytes); + } + + /** + * Gets the current position within the file + * + * @return the position within the file + */ + public int getPosition() throws IOException + { + // As all excel data structures are four bytes anyway, it's ok to + // truncate the long to an int + return (int) data.getFilePointer(); + } + + /** + * Sets the data at the specified position to the contents of the array + * + * @param pos the position to alter + * @param newdata the data to modify + */ + public void setData(byte[] newdata, int pos) throws IOException + { + long curpos = data.getFilePointer(); + data.seek(pos); + data.write(newdata); + data.seek(curpos); + } + + /** + * Writes the data to the output stream + */ + public void writeData(OutputStream out) throws IOException + { + byte[] buffer = new byte[1024]; + int length = 0; + data.seek(0); + while ((length = data.read(buffer)) != -1) + { + out.write(buffer, 0, length); + } + } + + /** + * Called when the final compound file has been written + */ + public void close() throws IOException + { + data.close(); + + // Explicitly delete the temporary file, since sometimes it is the case + // that a single process may be generating multiple different excel files + temporaryFile.delete(); + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/FooterRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/FooterRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/FooterRecord.java 17 Aug 2012 14:51:09 -0000 1.1 @@ -0,0 +1,89 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Places a string at the bottom of each page when the file is printed. + * JExcelApi sets this to be blank + */ +class FooterRecord extends WritableRecordData +{ + /** + * The binary data + */ + private byte[] data; + /** + * The footer string + */ + private String footer; + + /** + * Consructor + * + * @param s the footer + */ + public FooterRecord(String s) + { + super(Type.FOOTER); + + footer = s; + } + + /** + * Consructor invoked when copying a sheets + * + * @param fr the read footer record + */ + public FooterRecord(FooterRecord fr) + { + super(Type.FOOTER); + + footer = fr.footer; + } + + /** + * Gets the binary data to write to the output file + * + * @return the binary data + */ + public byte[] getData() + { + if (footer == null || footer.length() == 0) + { + data = new byte[0]; + return data; + } + + data = new byte[footer.length() * 2 + 3]; + IntegerHelper.getTwoBytes(footer.length(), data, 0); + data[2] = (byte) 0x1; + + StringHelper.getUnicodeBytes(footer, data, 3); + + return data; + } +} + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/FormulaRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/FormulaRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/FormulaRecord.java 17 Aug 2012 14:51:11 -0000 1.1 @@ -0,0 +1,378 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import common.Assert; +import common.Logger; + +import jxl.CellReferenceHelper; +import jxl.CellType; +import jxl.Sheet; +import jxl.WorkbookSettings; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; +import jxl.format.CellFormat; +import jxl.write.WritableCell; + +/** + * A formula record. Parses the string passed in to deduce the set of + * formula records + */ +public class FormulaRecord extends CellValue implements FormulaData +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(FormulaRecord.class); + + /** + * The formula to parse + */ + private String formulaToParse; + + /** + * The formula parser + */ + private FormulaParser parser; + + /** + * The parsed formula string + */ + private String formulaString; + + /** + * The parsed formula bytes + */ + private byte[] formulaBytes; + + /** + * The location where this formula was copied from. It is used subsequently + * to adjust relative cell references + */ + private CellValue copiedFrom; + + /** + * Constructor + * + * @param f the formula to copy + */ + public FormulaRecord(int c, int r, String f) + { + super(Type.FORMULA2, c, r); + formulaToParse = f; + copiedFrom = null; + } + + /** + * Constructor + * + * @param f the formula to copy + */ + public FormulaRecord(int c, int r, String f, CellFormat st) + { + super(Type.FORMULA, c, r, st); + formulaToParse = f; + copiedFrom = null; + } + + /** + * Copy constructor for writable formulas + * + * @param c the column + * @param r the row + * @param fr the record to copy + */ + protected FormulaRecord(int c, int r, FormulaRecord fr) + { + super(Type.FORMULA, c, r, fr); + copiedFrom = fr; + formulaBytes = new byte[fr.formulaBytes.length]; + System.arraycopy(fr.formulaBytes, 0, formulaBytes, 0, formulaBytes.length); + } + + /** + * Copy constructor for formulas read in - invoked from writable formulas + * + * @param c the column + * @param r the row + * @param rfr the formula data to copy + */ + protected FormulaRecord(int c, int r, ReadFormulaRecord rfr) + { + super(Type.FORMULA, c, r, rfr); + try + { + copiedFrom = rfr; + formulaBytes = rfr.getFormulaBytes(); + } + catch (FormulaException e) + { + // Fail silently + logger.error("", e); + } + } + + /** + * Initializes the string and the formula bytes. In order to get + * access to the workbook settings, the object is not initialized until + * it is added to the sheet + * + * @param ws the workbook settings + * @param es the external sheet + * @param nt the name table + */ + private void initialize(WorkbookSettings ws, ExternalSheet es, + WorkbookMethods nt) + { + if (copiedFrom != null) + { + initializeCopiedFormula(ws, es, nt); + return; + } + + parser = new FormulaParser(formulaToParse, es, nt, ws); + + try + { + parser.parse(); + formulaString = parser.getFormula(); + formulaBytes = parser.getBytes(); + } + catch (FormulaException e) + { + logger.warn + (e.getMessage() + + " when parsing formula " + formulaToParse + " in cell " + + getSheet().getName() + "!" + + CellReferenceHelper.getCellReference(getColumn(), getRow())); + + try + { + // try again, with an error formula + formulaToParse = "ERROR(1)"; + parser = new FormulaParser(formulaToParse, es, nt, ws); + parser.parse(); + formulaString = parser.getFormula(); + formulaBytes = parser.getBytes(); + } + catch (FormulaException e2) + { + // fail silently + logger.error("",e2); + } + } + } + + /** + * This formula was copied from a formula already present in the writable + * workbook. Requires special handling to sort out the cell references + + * @param ws the workbook settings + * @param es the external sheet + * @param nt the name table + */ + private void initializeCopiedFormula(WorkbookSettings ws, + ExternalSheet es, WorkbookMethods nt) + { + try + { + parser = new FormulaParser(formulaBytes, this, es, nt, ws); + parser.parse(); + parser.adjustRelativeCellReferences + (getColumn() - copiedFrom.getColumn(), + getRow() - copiedFrom.getRow()); + formulaString = parser.getFormula(); + formulaBytes = parser.getBytes(); + } + catch (FormulaException e) + { + try + { + // try again, with an error formula + formulaToParse = "ERROR(1)"; + parser = new FormulaParser(formulaToParse, es, nt, ws); + parser.parse(); + formulaString = parser.getFormula(); + formulaBytes = parser.getBytes(); + + } + catch (FormulaException e2) + { + // fail silently + logger.error("", e2); + } + } + } + + /** + * Called when the cell is added to the worksheet. Overrides the + * method in the base class in order to get a handle to the + * WorkbookSettings so that this formula may be initialized + * + * @param fr the formatting records + * @param ss the shared strings used within the workbook + * @param s the sheet this is being added to + */ + void setCellDetails(FormattingRecords fr, SharedStrings ss, + WritableSheetImpl s) + { + super.setCellDetails(fr, ss, s); + initialize(s.getWorkbookSettings(), s.getWorkbook(), s.getWorkbook()); + s.getWorkbook().addRCIRCell(this); + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + byte[] celldata = super.getData(); + byte[] formulaData = getFormulaData(); + byte[] data = new byte[formulaData.length + celldata.length]; + System.arraycopy(celldata, 0, data, 0, celldata.length); + System.arraycopy(formulaData, 0, data, celldata.length, + formulaData.length); + return data; + } + + /** + * Returns the content type of this cell + * + * @return the content type for this cell + */ + public CellType getType() + { + return CellType.ERROR; + } + + /** + * Quick and dirty function to return the contents of this cell as a string. + * For more complex manipulation of the contents, it is necessary to cast + * this interface to correct subinterface + * + * @return the contents of this cell as a string + */ + public String getContents() + { + return formulaString; + } + + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array + * + * @return the raw record data + */ + public byte[] getFormulaData() + { + byte[] data = new byte[formulaBytes.length + 16]; + System.arraycopy(formulaBytes, 0, data, 16, formulaBytes.length); + + data[6] = (byte) 0x10; + data[7] = (byte) 0x40; + data[12] = (byte) 0xe0; + data[13] = (byte) 0xfc; + // Set the recalculate on load bit + data[8] |= 0x02; + + // Set the length of the rpn array + IntegerHelper.getTwoBytes(formulaBytes.length, data, 14); + + return data; + } + + /** + * A dummy implementation to keep the compiler quiet. This object needs + * to be instantiated from ReadFormulaRecord + * + * @param col the column which the new cell will occupy + * @param row the row which the new cell will occupy + * @return NOTHING + */ + public WritableCell copyTo(int col, int row) + { + Assert.verify(false); + return null; + } + + /** + * Called when a column is inserted on the specified sheet. Notifies all + * RCIR cells of this change. The default implementation here does nothing + * + * @param s the sheet on which the column was inserted + * @param sheetIndex the sheet index on which the column was inserted + * @param col the column number which was inserted + */ + void columnInserted(Sheet s, int sheetIndex, int col) + { + parser.columnInserted(sheetIndex, col, s == getSheet()); + formulaBytes = parser.getBytes(); + } + + /** + * Called when a column is removed on the specified sheet. Notifies all + * RCIR cells of this change. The default implementation here does nothing + * + * @param s the sheet on which the column was inserted + * @param sheetIndex the sheet index on which the column was inserted + * @param col the column number which was inserted + */ + void columnRemoved(Sheet s, int sheetIndex, int col) + { + parser.columnRemoved(sheetIndex, col, s == getSheet()); + formulaBytes = parser.getBytes(); + } + + /** + * Called when a row is inserted on the specified sheet. Notifies all + * RCIR cells of this change. The default implementation here does nothing + * + * @param s the sheet on which the column was inserted + * @param sheetIndex the sheet index on which the column was inserted + * @param row the column number which was inserted + */ + void rowInserted(Sheet s, int sheetIndex, int row) + { + parser.rowInserted(sheetIndex, row, s == getSheet()); + formulaBytes = parser.getBytes(); + } + + /** + * Called when a row is inserted on the specified sheet. Notifies all + * RCIR cells of this change. The default implementation here does nothing + * + * @param s the sheet on which the row was removed + * @param sheetIndex the sheet index on which the column was removed + * @param row the column number which was removed + */ + void rowRemoved(Sheet s, int sheetIndex, int row) + { + parser.rowRemoved(sheetIndex, row, s == getSheet()); + formulaBytes = parser.getBytes(); + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/FunctionGroupCountRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/FunctionGroupCountRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/FunctionGroupCountRecord.java 17 Aug 2012 14:51:09 -0000 1.1 @@ -0,0 +1,67 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Stores the number of build in function groups + */ +class FunctionGroupCountRecord extends WritableRecordData +{ + /** + * The binary data + */ + private byte[] data; + + /** + * The number of built in function groups + */ + private int numFunctionGroups; + + /** + * Constructor + */ + public FunctionGroupCountRecord() + { + super(Type.FNGROUPCOUNT); + + numFunctionGroups = 0xe; + + data = new byte[2]; + + IntegerHelper.getTwoBytes(numFunctionGroups, data, 0); + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + return data; + } +} + + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/GridSetRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/GridSetRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/GridSetRecord.java 17 Aug 2012 14:51:12 -0000 1.1 @@ -0,0 +1,69 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which indicates that the user changed the stae of the + * GridLines option + */ +class GridSetRecord extends WritableRecordData +{ + /** + * The binary data + */ + private byte[] data; + /** + * Gridset flag + */ + private boolean gridSet; + + /** + * Constructor + * + * @param gs grid set flag + */ + public GridSetRecord(boolean gs) + { + super(Type.GRIDSET); + gridSet = gs; + + data = new byte[2]; + + if (gridSet) + { + data[0] = 1; + } + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + return data; + } +} + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/GuttersRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/GuttersRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/GuttersRecord.java 17 Aug 2012 14:51:13 -0000 1.1 @@ -0,0 +1,119 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which contains the size of the row and column gutters. These are + * all set to zero by default + */ +class GuttersRecord extends WritableRecordData +{ + /** + * The binary data + */ + private byte[] data; + /** + * The rowGutter + */ + private int rowGutter; + /** + * The column gutter + */ + private int colGutter; + /** + * The maximum outline level for the row gutter + */ + private int maxRowOutline; + /** + * The maximum row outline level for the column gutter + */ + private int maxColumnOutline; + + /** + * Constructor + */ + public GuttersRecord() + { + super(Type.GUTS); + } + + /** + * Gets the binary data for output + * + * @return the binary data + */ + public byte[] getData() + { + data = new byte[8]; + IntegerHelper.getTwoBytes(rowGutter, data, 0); + IntegerHelper.getTwoBytes(colGutter, data, 2); + IntegerHelper.getTwoBytes(maxRowOutline, data, 4); + IntegerHelper.getTwoBytes(maxColumnOutline, data, 6); + return data; + } + + /** + * Accessor for the maximum row outline + * + * @return the maximum row outline + */ + public int getMaxRowOutline() + { + return maxRowOutline; + } + + /** + * Sets the maximum row outline + * + * @param value the maximum row outline + */ + public void setMaxRowOutline(int value) + { + maxRowOutline = value; + rowGutter = 1 + 14 * value; + } + + /** + * Accessor for the maximum column outline + * + * @return the maximum column outline + */ + public int getMaxColumnOutline() + { + return maxColumnOutline; + } + + /** + * Sets the maximum column outline + * + * @param value the maximum row outline + */ + public void setMaxColumnOutline(int value) + { + maxColumnOutline = value; + colGutter = 1 + 14 * value; + } +} + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/HeaderRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/HeaderRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/HeaderRecord.java 17 Aug 2012 14:51:11 -0000 1.1 @@ -0,0 +1,88 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which specifies a print header for a work sheet + */ +class HeaderRecord extends WritableRecordData +{ + /** + * The binary data + */ + private byte[] data; + /** + * The print header string + */ + private String header; + + /** + * Constructor + * + * @param s the header string + */ + public HeaderRecord(String h) + { + super(Type.HEADER); + + header = h; + } + + /** + * Consructor invoked when copying a sheets + * + * @param hr the read header record + */ + public HeaderRecord(HeaderRecord hr) + { + super(Type.HEADER); + + header = hr.header; + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + if (header == null || header.length() == 0) + { + data = new byte[0]; + return data; + } + + data = new byte[header.length() * 2 + 3]; + IntegerHelper.getTwoBytes(header.length(), data, 0); + data[2] = (byte) 0x1; + + StringHelper.getUnicodeBytes(header, data, 3); + + return data; + } +} + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/HideobjRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/HideobjRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/HideobjRecord.java 17 Aug 2012 14:51:11 -0000 1.1 @@ -0,0 +1,67 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Stores options selected in the Options dialog box + */ +class HideobjRecord extends WritableRecordData +{ + /** + * Flag to hide everything + */ + private boolean hideAll; + /** + * The binary data + */ + private byte[] data; + + /** + * Constructor + * + * @param hide the hide all flag + */ + public HideobjRecord(boolean hide) + { + super(Type.HIDEOBJ); + + hideAll = hide; + data = new byte[2]; + + if (hideAll) + { + IntegerHelper.getTwoBytes(2, data, 0); + } + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + return data; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/HorizontalCentreRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/HorizontalCentreRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/HorizontalCentreRecord.java 17 Aug 2012 14:51:11 -0000 1.1 @@ -0,0 +1,70 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which indicates the whether the horizontal center option has + * been set + */ +class HorizontalCentreRecord extends WritableRecordData +{ + /** + * The binary data + */ + private byte[] data; + /** + * The centre flag + */ + private boolean centre; + + /** + * Constructor + * + * @param ce the centre flag + */ + public HorizontalCentreRecord(boolean ce) + { + super(Type.HCENTER); + + centre = ce; + + data = new byte[2]; + + if (centre) + { + data[0] = 1; + } + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + return data; + } +} + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/HorizontalPageBreaksRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/HorizontalPageBreaksRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/HorizontalPageBreaksRecord.java 17 Aug 2012 14:51:10 -0000 1.1 @@ -0,0 +1,72 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Contains the list of explicit horizontal page breaks on the current sheet + */ +class HorizontalPageBreaksRecord extends WritableRecordData +{ + /** + * The row breaks + */ + private int[] rowBreaks; + + /** + * Constructor + * + * @param break the row breaks + */ + public HorizontalPageBreaksRecord(int[] breaks) + { + super(Type.HORIZONTALPAGEBREAKS); + + rowBreaks = breaks; + } + + /** + * Gets the binary data to write to the output file + * + * @return the binary data + */ + public byte[] getData() + { + byte[] data = new byte[rowBreaks.length * 6 + 2]; + + // The number of breaks on the list + IntegerHelper.getTwoBytes(rowBreaks.length, data, 0); + int pos = 2; + + for (int i = 0; i < rowBreaks.length; i++) + { + IntegerHelper.getTwoBytes(rowBreaks[i], data, pos); + IntegerHelper.getTwoBytes(0xff, data, pos+4); + pos += 6; + } + + return data; + } +} + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/HyperlinkRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/HyperlinkRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/HyperlinkRecord.java 17 Aug 2012 14:51:08 -0000 1.1 @@ -0,0 +1,1277 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import java.io.File; +import java.net.URL; +import java.net.MalformedURLException; +import java.util.ArrayList; + +import common.Assert; +import common.Logger; + +import jxl.CellType; +import jxl.Hyperlink; +import jxl.Range; +import jxl.WorkbookSettings; +import jxl.biff.CellReferenceHelper; +import jxl.biff.IntegerHelper; +import jxl.biff.SheetRangeImpl; +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; +import jxl.write.Label; +import jxl.write.WritableHyperlink; +import jxl.write.WritableCell; +import jxl.write.WritableSheet; + + +/** + * A hyperlink + */ +public class HyperlinkRecord extends WritableRecordData +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(HyperlinkRecord.class); + + /** + * The first row + */ + private int firstRow; + /** + * The last row + */ + private int lastRow; + /** + * The first column + */ + private int firstColumn; + /** + * The last column + */ + private int lastColumn; + + /** + * The URL referred to by this hyperlink + */ + private URL url; + + /** + * The local file referred to by this hyperlink + */ + private File file; + + /** + * The location in this workbook referred to by this hyperlink + */ + private String location; + + /** + * The cell contents of the cell which activate this hyperlink + */ + private String contents; + + /** + * The type of this hyperlink + */ + private LinkType linkType; + + /** + * The data for this hyperlink + */ + private byte[] data; + + /** + * The range of this hyperlink. When creating a hyperlink, this will + * be null until the hyperlink is added to the sheet + */ + private Range range; + + /** + * The sheet containing this hyperlink + */ + private WritableSheet sheet; + + /** + * Indicates whether this record has been modified since it was copied + */ + private boolean modified; + + /** + * The excel type of hyperlink + */ + private static class LinkType {}; + + private static final LinkType urlLink = new LinkType(); + private static final LinkType fileLink = new LinkType(); + private static final LinkType uncLink = new LinkType(); + private static final LinkType workbookLink = new LinkType(); + private static final LinkType unknown = new LinkType(); + + /** + * Constructs this object from the readable spreadsheet + * + * @param hl the hyperlink from the read spreadsheet + */ + protected HyperlinkRecord(Hyperlink h, WritableSheet s) + { + super(Type.HLINK); + + if (h instanceof jxl.read.biff.HyperlinkRecord) + { + copyReadHyperlink(h, s); + } + else + { + copyWritableHyperlink(h, s); + } + } + + /** + * Copies a hyperlink read in from a read only sheet + */ + private void copyReadHyperlink(Hyperlink h, WritableSheet s) + { + jxl.read.biff.HyperlinkRecord hl = (jxl.read.biff.HyperlinkRecord) h; + + data = hl.getRecord().getData(); + sheet = s; + + // Populate this hyperlink with the copied data + firstRow = hl.getRow(); + firstColumn = hl.getColumn(); + lastRow = hl.getLastRow(); + lastColumn = hl.getLastColumn(); + range = new SheetRangeImpl(s, + firstColumn, firstRow, + lastColumn, lastRow); + + linkType = unknown; + + if (hl.isFile()) + { + linkType = fileLink; + file = hl.getFile(); + } + else if (hl.isURL()) + { + linkType = urlLink; + url = hl.getURL(); + } + else if (hl.isLocation()) + { + linkType = workbookLink; + location = hl.getLocation(); + } + + modified = false; + } + + /** + * Copies a hyperlink read in from a writable sheet. + * Used when copying writable sheets + * + * @param hl the hyperlink from the read spreadsheet + */ + private void copyWritableHyperlink(Hyperlink hl, WritableSheet s) + { + HyperlinkRecord h = (HyperlinkRecord) hl; + + firstRow = h.firstRow; + lastRow = h.lastRow; + firstColumn = h.firstColumn; + lastColumn = h.lastColumn; + + if (h.url != null) + { + try + { + url = new URL(h.url.toString()); + } + catch (MalformedURLException e) + { + // should never get a malformed url as a result url.toString() + Assert.verify(false); + } + } + + if (h.file != null) + { + file = new File(h.file.getPath()); + } + + location = h.location; + contents = h.contents; + linkType = h.linkType; + modified = true; + + sheet = s; + range = new SheetRangeImpl(s, + firstColumn, firstRow, + lastColumn, lastRow); + } + + /** + * Constructs a URL hyperlink to a range of cells + * + * @param col the column containing this hyperlink + * @param row the row containing this hyperlink + * @param lastcol the last column which activates this hyperlink + * @param lastrow the last row which activates this hyperlink + * @param url the hyperlink + * @param desc the description + */ + protected HyperlinkRecord(int col, int row, + int lastcol, int lastrow, + URL url, + String desc) + { + super(Type.HLINK); + + firstColumn = col; + firstRow = row; + + lastColumn = Math.max(firstColumn, lastcol); + lastRow = Math.max(firstRow, lastrow); + + this.url = url; + contents = desc; + + linkType = urlLink; + + modified = true; + } + + /** + * Constructs a File hyperlink to a range of cells + * + * @param col the column containing this hyperlink + * @param row the row containing this hyperlink + * @param lastcol the last column which activates this hyperlink + * @param lastrow the last row which activates this hyperlink + * @param file the hyperlink + * @param desc the description + */ + protected HyperlinkRecord(int col, int row, int lastcol, int lastrow, + File file, String desc) + { + super(Type.HLINK); + + firstColumn = col; + firstRow = row; + + lastColumn = Math.max(firstColumn, lastcol); + lastRow = Math.max(firstRow, lastrow); + contents = desc; + + this.file = file; + + if (file.getPath().startsWith("\\\\")) + { + linkType = uncLink; + } + else + { + linkType = fileLink; + } + + modified = true; + } + + /** + * Constructs a hyperlink to some cells within this workbook + * + * @param col the column containing this hyperlink + * @param row the row containing this hyperlink + * @param lastcol the last column which activates this hyperlink + * @param lastrow the last row which activates this hyperlink + * @param desc the contents of the cell which describe this hyperlink + * @param sheet the sheet containing the cells to be linked to + * @param destcol the column number of the first destination linked cell + * @param destrow the row number of the first destination linked cell + * @param lastdestcol the column number of the last destination linked cell + * @param lastdestrow the row number of the last destination linked cell + */ + protected HyperlinkRecord(int col, int row, + int lastcol, int lastrow, + String desc, + WritableSheet s, + int destcol, int destrow, + int lastdestcol, int lastdestrow) + { + super(Type.HLINK); + + firstColumn = col; + firstRow = row; + + lastColumn = Math.max(firstColumn, lastcol); + lastRow = Math.max(firstRow, lastrow); + + setLocation(s, destcol, destrow, lastdestcol, lastdestrow); + contents = desc; + + linkType = workbookLink; + + modified = true; + } + + /** + * Determines whether this is a hyperlink to a file + * + * @return TRUE if this is a hyperlink to a file, FALSE otherwise + */ + public boolean isFile() + { + return linkType == fileLink; + } + + /** + * Determines whether this is a hyperlink to a UNC + * + * @return TRUE if this is a hyperlink to a UNC, FALSE otherwise + */ + public boolean isUNC() + { + return linkType == uncLink; + } + + /** + * Determines whether this is a hyperlink to a web resource + * + * @return TRUE if this is a URL + */ + public boolean isURL() + { + return linkType == urlLink; + } + + /** + * Determines whether this is a hyperlink to a location in this workbook + * + * @return TRUE if this is a link to an internal location + */ + public boolean isLocation() + { + return linkType == workbookLink; + } + + /** + * Returns the row number of the top left cell + * + * @return the row number of this cell + */ + public int getRow() + { + return firstRow; + } + + /** + * Returns the column number of the top left cell + * + * @return the column number of this cell + */ + public int getColumn() + { + return firstColumn; + } + + /** + * Returns the row number of the bottom right cell + * + * @return the row number of this cell + */ + public int getLastRow() + { + return lastRow; + } + + /** + * Returns the column number of the bottom right cell + * + * @return the column number of this cell + */ + public int getLastColumn() + { + return lastColumn; + } + + /** + * Gets the URL referenced by this Hyperlink + * + * @return the URL, or NULL if this hyperlink is not a URL + */ + public URL getURL() + { + return url; + } + + /** + * Returns the local file eferenced by this Hyperlink + * + * @return the file, or NULL if this hyperlink is not a file + */ + public File getFile() + { + return file; + } + + /** + * Gets the binary data to be written to the output file + * + * @return the data to write to file + */ + public byte[] getData() + { + if (!modified) + { + return data; + } + + // Build up the common data + byte[] commonData = new byte[32]; + + // Set the range of cells this hyperlink applies to + IntegerHelper.getTwoBytes(firstRow, commonData, 0); + IntegerHelper.getTwoBytes(lastRow, commonData, 2); + IntegerHelper.getTwoBytes(firstColumn, commonData, 4); + IntegerHelper.getTwoBytes(lastColumn, commonData, 6); + + // Some inexplicable byte sequence + commonData[8] = (byte) 0xd0; + commonData[9] = (byte) 0xc9; + commonData[10] = (byte) 0xea; + commonData[11] = (byte) 0x79; + commonData[12] = (byte) 0xf9; + commonData[13] = (byte) 0xba; + commonData[14] = (byte) 0xce; + commonData[15] = (byte) 0x11; + commonData[16] = (byte) 0x8c; + commonData[17] = (byte) 0x82; + commonData[18] = (byte) 0x0; + commonData[19] = (byte) 0xaa; + commonData[20] = (byte) 0x0; + commonData[21] = (byte) 0x4b; + commonData[22] = (byte) 0xa9; + commonData[23] = (byte) 0x0b; + commonData[24] = (byte) 0x2; + commonData[25] = (byte) 0x0; + commonData[26] = (byte) 0x0; + commonData[27] = (byte) 0x0; + + // Set up the option flags to indicate the type of this URL. There + // is no description + int optionFlags = 0; + if (isURL()) + { + optionFlags = 3; + + if (contents != null) + { + optionFlags |= 0x14; + } + } + else if (isFile()) + { + optionFlags = 1; + + if (contents != null) + { + optionFlags |= 0x14; + } + } + else if (isLocation()) + { + optionFlags = 8; + } + else if (isUNC()) + { + optionFlags = 259; + } + + IntegerHelper.getFourBytes(optionFlags, commonData, 28); + + if (isURL()) + { + data = getURLData(commonData); + } + else if (isFile()) + { + data = getFileData(commonData); + } + else if (isLocation()) + { + data = getLocationData(commonData); + } + else if (isUNC()) + { + data = getUNCData(commonData); + } + + return data; + } + + /** + * A standard toString method + * + * @return the contents of this object as a string + */ + public String toString() + { + if (isFile()) + { + return file.toString(); + } + else if (isURL()) + { + return url.toString(); + } + else if (isUNC()) + { + return file.toString(); + } + else + { + return ""; + } + } + + /** + * Gets the range of cells which activate this hyperlink + * The get sheet index methods will all return -1, because the + * cells will all be present on the same sheet + * + * @return the range of cells which activate the hyperlink or NULL + * if this hyperlink has not been added to the sheet + */ + public Range getRange() + { + return range; + } + + /** + * Sets the URL of this hyperlink + * + * @param url the url + */ + public void setURL(URL url) + { + URL prevurl = this.url; + linkType = urlLink; + file = null; + location = null; + contents = null; + this.url = url; + modified = true; + + if (sheet == null) + { + // hyperlink has not been added to the sheet yet, so simply return + return; + } + + // Change the label on the sheet if it was a string representation of the + // URL + WritableCell wc = sheet.getWritableCell(firstColumn, firstRow); + + if (wc.getType() == CellType.LABEL) + { + Label l = (Label) wc; + String prevurlString = prevurl.toString(); + String prevurlString2 = ""; + if (prevurlString.charAt(prevurlString.length() - 1) == '/' || + prevurlString.charAt(prevurlString.length() - 1) == '\\') + { + prevurlString2 = prevurlString.substring(0, + prevurlString.length() - 1); + } + + if (l.getString().equals(prevurlString) || + l.getString().equals(prevurlString2)) + { + l.setString(url.toString()); + } + } + } + + /** + * Sets the file activated by this hyperlink + * + * @param file the file + */ + public void setFile(File file) + { + linkType = fileLink; + url = null; + location = null; + contents = null; + this.file = file; + modified = true; + + if (sheet == null) + { + // hyperlink has not been added to the sheet yet, so simply return + return; + } + + // Change the label on the sheet + WritableCell wc = sheet.getWritableCell(firstColumn, firstRow); + + Assert.verify(wc.getType() == CellType.LABEL); + + Label l = (Label) wc; + l.setString(file.toString()); + } + + /** + * Sets the location of the cells to be linked to within this workbook + * + * @param desc the label describing the link + * @param sheet the sheet containing the cells to be linked to + * @param destcol the column number of the first destination linked cell + * @param destrow the row number of the first destination linked cell + * @param lastdestcol the column number of the last destination linked cell + * @param lastdestrow the row number of the last destination linked cell + */ + protected void setLocation(String desc, + WritableSheet sheet, + int destcol, int destrow, + int lastdestcol, int lastdestrow) + { + linkType = workbookLink; + url = null; + file = null; + modified = true; + contents = desc; + + setLocation(sheet, destcol, destrow, lastdestcol, lastdestrow); + + if (sheet == null) + { + // hyperlink has not been added to the sheet yet, so simply return + return; + } + + // Change the label on the sheet + WritableCell wc = sheet.getWritableCell(firstColumn, firstRow); + + Assert.verify(wc.getType() == CellType.LABEL); + + Label l = (Label) wc; + l.setString(desc); + } + + /** + * Initializes the location from the data passed in + * + * @param sheet the sheet containing the cells to be linked to + * @param destcol the column number of the first destination linked cell + * @param destrow the row number of the first destination linked cell + * @param lastdestcol the column number of the last destination linked cell + * @param lastdestrow the row number of the last destination linked cell + */ + private void setLocation(WritableSheet sheet, + int destcol, int destrow, + int lastdestcol, int lastdestrow) + { + StringBuffer sb = new StringBuffer(); + sb.append('\''); + + if (sheet.getName().indexOf('\'') == -1) + { + sb.append(sheet.getName()); + } + else + { + // sb.append(sheet.getName().replaceAll("'", "''")); + + // Can't use replaceAll as it is only 1.4 compatible, so have to + // do this the tedious way + String sheetName = sheet.getName(); + int pos = 0 ; + int nextPos = sheetName.indexOf('\'', pos); + + while (nextPos != -1 && pos < sheetName.length()) + { + sb.append(sheetName.substring(pos, nextPos)); + sb.append("''"); + pos = nextPos + 1; + nextPos = sheetName.indexOf('\'', pos); + } + sb.append(sheetName.substring(pos)); + } + + sb.append('\''); + sb.append('!'); + + lastdestcol = Math.max(destcol, lastdestcol); + lastdestrow = Math.max(destrow, lastdestrow); + + CellReferenceHelper.getCellReference(destcol, destrow, sb); + sb.append(':'); + CellReferenceHelper.getCellReference(lastdestcol, lastdestrow, sb); + + location = sb.toString(); + } + + /** + * A row has been inserted, so adjust the range objects accordingly + * + * @param r the row which has been inserted + */ + void insertRow(int r) + { + // This will not be called unless the hyperlink has been added to the + // sheet + Assert.verify(sheet != null && range != null); + + if (r > lastRow) + { + return; + } + + if (r <= firstRow) + { + firstRow++; + modified = true; + } + + if (r <= lastRow) + { + lastRow++; + modified = true; + } + + if (modified) + { + range = new SheetRangeImpl(sheet, + firstColumn, firstRow, + lastColumn, lastRow); + } + } + + /** + * A column has been inserted, so adjust the range objects accordingly + * + * @param c the column which has been inserted + */ + void insertColumn(int c) + { + // This will not be called unless the hyperlink has been added to the + // sheet + Assert.verify(sheet != null && range != null); + + if (c > lastColumn) + { + return; + } + + if (c <= firstColumn) + { + firstColumn++; + modified = true; + } + + if (c <= lastColumn) + { + lastColumn++; + modified = true; + } + + if (modified) + { + range = new SheetRangeImpl(sheet, + firstColumn, firstRow, + lastColumn, lastRow); + } + } + + /** + * A row has been removed, so adjust the range objects accordingly + * + * @param r the row which has been inserted + */ + void removeRow(int r) + { + // This will not be called unless the hyperlink has been added to the + // sheet + Assert.verify(sheet != null && range != null); + + if (r > lastRow) + { + return; + } + + if (r < firstRow) + { + firstRow--; + modified = true; + } + + if (r < lastRow) + { + lastRow--; + modified = true; + } + + if (modified) + { + Assert.verify(range != null); + range = new SheetRangeImpl(sheet, + firstColumn, firstRow, + lastColumn, lastRow); + } + } + + /** + * A column has been removed, so adjust the range objects accordingly + * + * @param c the column which has been removed + */ + void removeColumn(int c) + { + // This will not be called unless the hyperlink has been added to the + // sheet + Assert.verify(sheet != null && range != null); + + if (c > lastColumn) + { + return; + } + + if (c < firstColumn) + { + firstColumn--; + modified = true; + } + + if (c < lastColumn) + { + lastColumn--; + modified = true; + } + + if (modified) + { + Assert.verify(range != null); + range = new SheetRangeImpl(sheet, + firstColumn, firstRow, + lastColumn, lastRow); + } + } + + /** + * Gets the hyperlink stream specific to a URL link + * + * @param cd the data common for all types of hyperlink + * @return the raw data for a URL hyperlink + */ + private byte[] getURLData(byte[] cd) + { + String urlString = url.toString(); + + int dataLength = cd.length + 20 + (urlString.length() + 1)* 2; + + if (contents != null) + { + dataLength += 4 + (contents.length() + 1) * 2; + } + + byte[] d = new byte[dataLength]; + + System.arraycopy(cd, 0, d, 0, cd.length); + + int urlPos = cd.length; + + if (contents != null) + { + IntegerHelper.getFourBytes(contents.length() + 1, d, urlPos); + StringHelper.getUnicodeBytes(contents, d, urlPos + 4); + urlPos += (contents.length() + 1) * 2 + 4; + } + + // Inexplicable byte sequence + d[urlPos] = (byte) 0xe0; + d[urlPos+1] = (byte) 0xc9; + d[urlPos+2] = (byte) 0xea; + d[urlPos+3] = (byte) 0x79; + d[urlPos+4] = (byte) 0xf9; + d[urlPos+5] = (byte) 0xba; + d[urlPos+6] = (byte) 0xce; + d[urlPos+7] = (byte) 0x11; + d[urlPos+8] = (byte) 0x8c; + d[urlPos+9] = (byte) 0x82; + d[urlPos+10] = (byte) 0x0; + d[urlPos+11] = (byte) 0xaa; + d[urlPos+12] = (byte) 0x0; + d[urlPos+13] = (byte) 0x4b; + d[urlPos+14] = (byte) 0xa9; + d[urlPos+15] = (byte) 0x0b; + + // Number of characters in the url, including a zero trailing character + IntegerHelper.getFourBytes((urlString.length() + 1)*2, d, urlPos+16); + + // Put the url into the data string + StringHelper.getUnicodeBytes(urlString, d, urlPos+20); + + return d; + } + + /** + * Gets the hyperlink stream specific to a URL link + * + * @param cd the data common for all types of hyperlink + * @return the raw data for a URL hyperlink + */ + private byte[] getUNCData(byte[] cd) + { + String uncString = file.getPath(); + + byte[] d = new byte[cd.length + uncString.length() * 2 + 2 + 4]; + System.arraycopy(cd, 0, d, 0, cd.length); + + int urlPos = cd.length; + + // The length of the unc string, including zero terminator + int length = uncString.length() + 1; + IntegerHelper.getFourBytes(length, d, urlPos); + + // Place the string into the stream + StringHelper.getUnicodeBytes(uncString, d, urlPos + 4); + + return d; + } + + /** + * Gets the hyperlink stream specific to a local file link + * + * @param cd the data common for all types of hyperlink + * @return the raw data for a URL hyperlink + */ + private byte[] getFileData(byte[] cd) + { + // Build up the directory hierarchy in reverse order + ArrayList path = new ArrayList(); + ArrayList shortFileName = new ArrayList(); + path.add(file.getName()); + shortFileName.add(getShortName(file.getName())); + + File parent = file.getParentFile(); + while (parent != null) + { + path.add(parent.getName()); + shortFileName.add(getShortName(parent.getName())); + parent = parent.getParentFile(); + } + + // Deduce the up directory level count and remove the directory from + // the path + int upLevelCount = 0; + int pos = path.size() - 1; + boolean upDir = true; + + while (upDir) + { + String s = (String) path.get(pos); + if (s.equals("..")) + { + upLevelCount++; + path.remove(pos); + shortFileName.remove(pos); + } + else + { + upDir = false; + } + + pos--; + } + + StringBuffer filePathSB = new StringBuffer(); + StringBuffer shortFilePathSB = new StringBuffer(); + + if (file.getPath().charAt(1)==':') + { + char driveLetter = file.getPath().charAt(0); + if (driveLetter != 'C' && driveLetter != 'c') + { + filePathSB.append(driveLetter); + filePathSB.append(':'); + shortFilePathSB.append(driveLetter); + shortFilePathSB.append(':'); + } + } + + for (int i = path.size() - 1; i >= 0 ; i--) + { + filePathSB.append((String)path.get(i)); + shortFilePathSB.append((String)shortFileName.get(i)); + + if (i != 0) + { + filePathSB.append("\\"); + shortFilePathSB.append("\\"); + } + } + + + String filePath = filePathSB.toString(); + String shortFilePath = shortFilePathSB.toString(); + + int dataLength = cd.length + + 4 + (shortFilePath.length() + 1) + // short file name + 16 + // inexplicable byte sequence + 2 + // up directory level count + 8 + (filePath.length() + 1) * 2 + // long file name + 24; // inexplicable byte sequence + + + if (contents != null) + { + dataLength += 4 + (contents.length() + 1) * 2; + } + + // Copy across the common data into the new array + byte[] d = new byte[dataLength]; + + System.arraycopy(cd, 0, d, 0, cd.length); + + int filePos = cd.length; + + // Add in the description text + if (contents != null) + { + IntegerHelper.getFourBytes(contents.length() + 1, d, filePos); + StringHelper.getUnicodeBytes(contents, d, filePos + 4); + filePos += (contents.length() + 1) * 2 + 4; + } + + int curPos = filePos; + + // Inexplicable byte sequence + d[curPos] = (byte) 0x03; + d[curPos+1] = (byte) 0x03; + d[curPos+2] = (byte) 0x0; + d[curPos+3] = (byte) 0x0; + d[curPos+4] = (byte) 0x0; + d[curPos+5] = (byte) 0x0; + d[curPos+6] = (byte) 0x0; + d[curPos+7] = (byte) 0x0; + d[curPos+8] = (byte) 0xc0; + d[curPos+9] = (byte) 0x0; + d[curPos+10] = (byte) 0x0; + d[curPos+11] = (byte) 0x0; + d[curPos+12] = (byte) 0x0; + d[curPos+13] = (byte) 0x0; + d[curPos+14] = (byte) 0x0; + d[curPos+15] = (byte) 0x46; + + curPos += 16; + + // The directory up level count + IntegerHelper.getTwoBytes(upLevelCount, d, curPos); + curPos += 2; + + // The number of bytes in the short file name, including zero terminator + IntegerHelper.getFourBytes((shortFilePath.length() + 1), d, curPos); + + // The short file name + StringHelper.getBytes(shortFilePath, d, curPos+4); + + curPos += 4 + (shortFilePath.length() + 1); + + // Inexplicable byte sequence + d[curPos] = (byte) 0xff; + d[curPos+1] = (byte) 0xff; + d[curPos+2] = (byte) 0xad; + d[curPos+3] = (byte) 0xde; + d[curPos+4] = (byte) 0x0; + d[curPos+5] = (byte) 0x0; + d[curPos+6] = (byte) 0x0; + d[curPos+7] = (byte) 0x0; + d[curPos+8] = (byte) 0x0; + d[curPos+9] = (byte) 0x0; + d[curPos+10] = (byte) 0x0; + d[curPos+11] = (byte) 0x0; + d[curPos+12] = (byte) 0x0; + d[curPos+13] = (byte) 0x0; + d[curPos+14] = (byte) 0x0; + d[curPos+15] = (byte) 0x0; + d[curPos+16] = (byte) 0x0; + d[curPos+17] = (byte) 0x0; + d[curPos+18] = (byte) 0x0; + d[curPos+19] = (byte) 0x0; + d[curPos+20] = (byte) 0x0; + d[curPos+21] = (byte) 0x0; + d[curPos+22] = (byte) 0x0; + d[curPos+23] = (byte) 0x0; + + curPos += 24; + + // Size of the long file name data in bytes, including inexplicable data + // fields + int size = 6 + filePath.length() * 2; + IntegerHelper.getFourBytes(size, d, curPos); + curPos += 4; + + // The number of bytes in the long file name + // NOT including zero terminator + IntegerHelper.getFourBytes((filePath.length()) * 2, d, curPos); + curPos += 4; + + // Inexplicable bytes + d[curPos] = (byte) 0x3; + d[curPos+1] = (byte) 0x0; + + curPos += 2; + + // The long file name + StringHelper.getUnicodeBytes(filePath, d, curPos); + curPos += (filePath.length() + 1) * 2; + + + /* + curPos += 24; + int nameLength = filePath.length() * 2; + + // Size of the file link + IntegerHelper.getFourBytes(nameLength+6, d, curPos); + + // Number of characters + IntegerHelper.getFourBytes(nameLength, d, curPos+4); + + // Inexplicable byte sequence + d[curPos+8] = 0x03; + + // The long file name + StringHelper.getUnicodeBytes(filePath, d, curPos+10); + */ + + return d; + } + + /** + * Gets the DOS short file name in 8.3 format of the name passed in + * + * @param s the name + * @return the dos short name + */ + private String getShortName(String s) + { + int sep = s.indexOf('.'); + + String prefix = null; + String suffix = null; + + if (sep == -1) + { + prefix = s; + suffix=""; + } + else + { + prefix = s.substring(0,sep); + suffix = s.substring(sep+1); + } + + if (prefix.length() > 8) + { + prefix = prefix.substring(0, 6) + "~" + (prefix.length() - 8); + prefix = prefix.substring(0, 8); + } + + suffix = suffix.substring(0,Math.min(3, suffix.length())); + + if (suffix.length() > 0) + { + return prefix + '.' + suffix; + } + else + { + return prefix; + } + } + + /** + * Gets the hyperlink stream specific to a location link + * + * @param cd the data common for all types of hyperlink + * @return the raw data for a URL hyperlink + */ + private byte[] getLocationData(byte[] cd) + { + byte[] d = new byte[cd.length + 4 + (location.length() + 1)* 2]; + System.arraycopy(cd, 0, d, 0, cd.length); + + int locPos = cd.length; + + // The number of chars in the location string, plus a 0 terminator + IntegerHelper.getFourBytes(location.length() + 1, d, locPos); + + // Get the location + StringHelper.getUnicodeBytes(location, d, locPos+4); + + return d; + } + + + /** + * Initializes the range when this hyperlink is added to the sheet + * + * @param s the sheet containing this hyperlink + */ + void initialize(WritableSheet s) + { + sheet = s; + range = new SheetRangeImpl(s, + firstColumn, firstRow, + lastColumn, lastRow); + } + + /** + * Called by the worksheet. Gets the string contents to put into the cell + * containing this hyperlink + * + * @return the string contents for the hyperlink cell + */ + String getContents() + { + return contents; + } + + /** + * Sets the description + * + * @param desc the description + */ + protected void setContents(String desc) + { + contents = desc; + modified = true; + } +} + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/IndexRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/IndexRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/IndexRecord.java 17 Aug 2012 14:51:12 -0000 1.1 @@ -0,0 +1,104 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.IntegerHelper; +import jxl.biff.WritableRecordData; + +/** + * Index into the cell rows in an worksheet + */ +class IndexRecord extends WritableRecordData +{ + /** + * The binary data + */ + private byte[] data; + /** + * The numbe of rows served by this index record + */ + private int rows; + /** + * The position of the BOF record in the excel output stream + */ + private int bofPosition; + /** + * The number of blocks needed to hold all the rows + */ + private int blocks; + + /** + * The position of the current 'pointer' within the byte array + */ + private int dataPos; + + /** + * Constructor + * + * @param pos the position of the BOF record + * @param bl the number of blocks + * @param r the number of rows + */ + public IndexRecord(int pos, int r, int bl) + { + super(Type.INDEX); + bofPosition = pos; + rows = r; + blocks = bl; + + // Allocate the amount of bytes required to hold all the blocks + data = new byte[16 + 4 * blocks]; + dataPos = 16; + } + + /** + * Gets the binary data for output. This writes out an empty data block, and + * the information is filled in later on when the information becomes + * available + * + * @return the binary data + */ + protected byte[] getData() + { + IntegerHelper.getFourBytes(rows, data, 8); + return data; + } + + /** + * Adds another index record into the array + * + * @param pos the position in the output file + */ + void addBlockPosition(int pos) + { + IntegerHelper.getFourBytes(pos - bofPosition, data, dataPos); + dataPos += 4; + } + + /** + * Sets the position of the data start. This happens to be the position + * of the DEFCOLWIDTH record + */ + void setDataStartPosition(int pos) + { + IntegerHelper.getFourBytes(pos - bofPosition, data, 12); + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/InterfaceEndRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/InterfaceEndRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/InterfaceEndRecord.java 17 Aug 2012 14:51:12 -0000 1.1 @@ -0,0 +1,48 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Marks the end of the user interface section of the Book stream. + * It has no data field + */ +class InterfaceEndRecord extends WritableRecordData +{ + /** + * Consructor + */ + public InterfaceEndRecord() + { + super(Type.INTERFACEEND); + } + + /** + * Gets the binary data for output + * + * @return the binary data + */ + public byte[] getData() + { + return new byte[0]; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/InterfaceHeaderRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/InterfaceHeaderRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/InterfaceHeaderRecord.java 17 Aug 2012 14:51:11 -0000 1.1 @@ -0,0 +1,52 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Marks the beginning of the user interface record + */ +class InterfaceHeaderRecord extends WritableRecordData +{ + /** + * Constructor + */ + public InterfaceHeaderRecord() + { + super(Type.INTERFACEHDR); + } + + /** + * Gets the binary data + * + * @return the binary data + */ + public byte[] getData() + { + // Return the character encoding + byte[] data = new byte[] + { (byte) 0xb0, (byte) 0x04}; + return data; + } +} + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/IterationRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/IterationRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/IterationRecord.java 17 Aug 2012 14:51:11 -0000 1.1 @@ -0,0 +1,69 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Stores the iteration option from the dialog box + */ +class IterationRecord extends WritableRecordData +{ + /** + * The iterate flag + */ + private boolean iterate; + /** + * The binary data + */ + private byte[] data; + + /** + * Constructor + * + * @param it the iterator flag + */ + public IterationRecord(boolean it) + { + super(Type.ITERATION); + + iterate = it; + + data = new byte[2]; + + if (iterate) + { + data[0] = 1; + } + } + + /** + * Gets the binary data for output + * + * @return the binary data + */ + public byte[] getData() + { + return data; + } +} + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/JxlWriteException.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/JxlWriteException.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/JxlWriteException.java 17 Aug 2012 14:51:10 -0000 1.1 @@ -0,0 +1,71 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.write.WriteException; + +/** + * Exception thrown when reading a biff file + */ +public class JxlWriteException extends WriteException +{ + private static class WriteMessage + { + /** + */ + public String message; + /** + * Constructs this exception with the specified message + * + * @param m the messageA + */ + WriteMessage(String m) {message = m;} + } + + /** + */ + static WriteMessage formatInitialized = + new WriteMessage("Attempt to modify a referenced format"); + /** + */ + static WriteMessage cellReferenced = + new WriteMessage("Cell has already been added to a worksheet"); + + static WriteMessage maxRowsExceeded = + new WriteMessage("The maximum number of rows permitted on a worksheet " + + "been exceeded"); + + static WriteMessage maxColumnsExceeded = + new WriteMessage("The maximum number of columns permitted on a " + + "worksheet has been exceeded"); + + static WriteMessage copyPropertySets = + new WriteMessage("Error encounted when copying additional property sets"); + + /** + * Constructs this exception with the specified message + * + * @param m the message + */ + public JxlWriteException(WriteMessage m) + { + super(m.message); + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/LabelRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/LabelRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/LabelRecord.java 17 Aug 2012 14:51:08 -0000 1.1 @@ -0,0 +1,231 @@ +/********************************************************************* +* +* Copyright (C) 2001 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import common.Assert; +import common.Logger; + +import jxl.CellType; +import jxl.LabelCell; +import jxl.biff.FormattingRecords; +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.format.CellFormat; + +/** + * A label record, used for writing out string + */ +public abstract class LabelRecord extends CellValue +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(LabelRecord.class); + + /** + * The string + */ + private String contents; + + /** + * A handle to the shared strings used within this workbook + */ + private SharedStrings sharedStrings; + + /** + * The index of the string in the shared string table + */ + private int index; + + /** + * Constructor used when creating a label from the user API + * + * @param c the column + * @param cont the contents + * @param r the row + */ + protected LabelRecord(int c, int r, String cont) + { + super(Type.LABELSST, c, r); + contents = cont; + if (contents == null) + { + contents=""; + } + } + + /** + * Constructor used when creating a label from the API. This is + * overloaded to allow formatting information to be passed to the record + * + * @param c the column + * @param cont the contents + * @param r the row + * @param st the format applied to the cell + */ + protected LabelRecord(int c, int r, String cont, CellFormat st) + { + super(Type.LABELSST, c, r, st); + contents = cont; + + if (contents == null) + { + contents=""; + } + } + + + /** + * Copy constructor + * + * @param c the column + * @param r the row + * @param nr the record to copy + */ + protected LabelRecord(int c, int r, LabelRecord lr) + { + super(Type.LABELSST, c, r, lr); + contents = lr.contents; + } + + /** + * Constructor used when copying a label from a read only + * spreadsheet + * + * @param lc the label to copy + */ + protected LabelRecord(LabelCell lc) + { + super(Type.LABELSST, lc); + contents = lc.getString(); + if (contents == null) + { + contents = ""; + } + } + + /** + * Returns the content type of this cell + * + * @return the content type for this cell + */ + public CellType getType() + { + return CellType.LABEL; + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + byte[] celldata = super.getData(); + byte[] data = new byte[celldata.length + 4]; + System.arraycopy(celldata, 0, data, 0, celldata.length); + IntegerHelper.getFourBytes(index, data, celldata.length); + + return data; + } + + /** + * Quick and dirty function to return the contents of this cell as a string. + * For more complex manipulation of the contents, it is necessary to cast + * this interface to correct subinterface + * + * @return the contents of this cell as a string + */ + public String getContents() + { + return contents; + } + + /** + * Gets the label for this cell. The value returned will be the same + * as for the getContents method in the base class + * + * @return the cell contents + */ + public String getString() + { + return contents; + } + + /** + * Sets the string contents of this cell + * + * @param s the new string contents + */ + protected void setString(String s) + { + if (s == null) + { + s = ""; + } + + contents = s; + + // Don't bother doing anything if this cell has not been referenced + // yet - everything will be set up in due course + if (!isReferenced()) + { + return; + } + + Assert.verify(sharedStrings != null); + + // Initalize the shared string index + index = sharedStrings.getIndex(contents); + + // Use the sharedStrings reference instead of this object's own + // handle - this means that the bespoke copy becomes eligible for + // garbage collection + contents = sharedStrings.get(index); + } + + /** + * Overrides the method in the base class in order to add the string + * content to the shared string table, and to store its shared string + * index + * + * @param fr the formatting records + * @param ss the shared strings used within the workbook + * @param s + */ + void setCellDetails(FormattingRecords fr, SharedStrings ss, + WritableSheetImpl s) + { + super.setCellDetails(fr, ss, s); + + sharedStrings = ss; + + index = sharedStrings.getIndex(contents); + + // Use the sharedStrings reference instead of this object's own + // handle - this means that the bespoke copy becomes eligible for + // garbage collection + contents = sharedStrings.get(index); + } + +} + + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/LeftMarginRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/LeftMarginRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/LeftMarginRecord.java 17 Aug 2012 14:51:11 -0000 1.1 @@ -0,0 +1,33 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; + +/** + * The settings for the left margin + */ +class LeftMarginRecord extends MarginRecord +{ + LeftMarginRecord(double v) + { + super(Type.LEFTMARGIN, v); + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/MMSRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/MMSRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/MMSRecord.java 17 Aug 2012 14:51:12 -0000 1.1 @@ -0,0 +1,71 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Stores the number of addmen and delmenu groups in the book stream + */ +class MMSRecord extends WritableRecordData +{ + /** + * The number of menu items added + */ + private byte numMenuItemsAdded; + /** + * The number of menu items deleted + */ + private byte numMenuItemsDeleted; + /** + * The binary data + */ + private byte[] data; + + /** + * Constructor + * + * @param menuItemsAdded the number of menu items added + * @param menuItemsDeleted the number of menu items deleted + */ + public MMSRecord(int menuItemsAdded, int menuItemsDeleted) + { + super(Type.MMS); + + numMenuItemsAdded = (byte) menuItemsAdded; + numMenuItemsDeleted = (byte) menuItemsDeleted; + + data = new byte[2]; + + data[0] = numMenuItemsAdded; + data[1] = numMenuItemsDeleted; + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + return data; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/MarginRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/MarginRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/MarginRecord.java 17 Aug 2012 14:51:12 -0000 1.1 @@ -0,0 +1,59 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.DoubleHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which specifies a margin value + */ +abstract class MarginRecord extends WritableRecordData +{ + /** + * The margin + */ + private double margin; + + /** + * Constructor + */ + public MarginRecord(Type t, double v) + { + super(t); + margin = v; + } + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + byte[] data = new byte[8]; + + DoubleHelper.getIEEEBytes(margin, data, 0); + + return data; + } +} + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/MemoryDataOutput.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/MemoryDataOutput.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/MemoryDataOutput.java 17 Aug 2012 14:51:12 -0000 1.1 @@ -0,0 +1,117 @@ +/********************************************************************* +* +* Copyright (C) 2007 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import java.io.OutputStream; +import java.io.IOException; + +import common.Logger; + +/** + * Used to generate the excel biff data in memory. This class wraps a byte + * array + */ +class MemoryDataOutput implements ExcelDataOutput +{ + // The logger + private static Logger logger = Logger.getLogger(MemoryDataOutput.class); + + /** + * The excel data + */ + private byte[] data; + + /** + * The grow size for the array + */ + private int growSize; + + /** + * The current position within the array + */ + private int pos; + + /** + * Constructor + */ + public MemoryDataOutput(int initialSize, int gs) + { + data = new byte[initialSize]; + growSize = gs; + pos = 0; + } + + /** + * Writes the bytes to the end of the array, growing the array + * as needs dictate + * + * @param d the data to write to the end of the array + */ + public void write(byte[] bytes) + { + while (pos + bytes.length > data.length) + { + // Grow the array + byte[] newdata = new byte[data.length + growSize]; + System.arraycopy(data, 0, newdata, 0, pos); + data = newdata; + } + + System.arraycopy(bytes, 0, data, pos, bytes.length); + pos += bytes.length; + } + + /** + * Gets the current position within the file + * + * @return the position within the file + */ + public int getPosition() + { + return pos; + } + + /** + * Sets the data at the specified position to the contents of the array + * + * @param pos the position to alter + * @param newdata the data to modify + */ + public void setData(byte[] newdata, int pos) + { + System.arraycopy(newdata, 0, data, pos, newdata.length); + } + + /** + * Writes the data to the output stream + */ + public void writeData(OutputStream out) throws IOException + { + out.write(data, 0, pos); + } + + /** + * Called when the final compound file has been written. No cleanup is + * necessary for in-memory file generation + */ + public void close() throws IOException + { + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/MergedCells.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/MergedCells.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/MergedCells.java 17 Aug 2012 14:51:11 -0000 1.1 @@ -0,0 +1,326 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; + +import common.Assert; +import common.Logger; + +import jxl.Cell; +import jxl.CellType; +import jxl.Range; +import jxl.WorkbookSettings; +import jxl.biff.SheetRangeImpl; +import jxl.write.Blank; +import jxl.write.WritableSheet; +import jxl.write.WriteException; + +/** + * Contains all the merged cells, and the necessary logic for checking + * for intersections and for handling very large amounts of merging + */ +class MergedCells +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(MergedCells.class); + + /** + * The list of merged cells + */ + private ArrayList ranges; + + /** + * The sheet containing the cells + */ + private WritableSheet sheet; + + /** + * The maximum number of ranges per sheet + */ + private static final int maxRangesPerSheet = 1020; + + /** + * Constructor + */ + public MergedCells(WritableSheet ws) + { + ranges = new ArrayList(); + sheet = ws; + } + + /** + * Adds the range to the list of merged cells. Does no checking + * at this stage + * + * @param range the range to add + */ + void add(Range r) + { + ranges.add(r); + } + + /** + * Used to adjust the merged cells following a row insertion + */ + void insertRow(int row) + { + // Adjust any merged cells + SheetRangeImpl sr = null; + Iterator i = ranges.iterator(); + while (i.hasNext()) + { + sr = (SheetRangeImpl) i.next(); + sr.insertRow(row); + } + } + + /** + * Used to adjust the merged cells following a column insertion + */ + void insertColumn(int col) + { + SheetRangeImpl sr = null; + Iterator i = ranges.iterator(); + while (i.hasNext()) + { + sr = (SheetRangeImpl) i.next(); + sr.insertColumn(col); + } + } + + /** + * Used to adjust the merged cells following a column removal + */ + void removeColumn(int col) + { + SheetRangeImpl sr = null; + Iterator i = ranges.iterator(); + while (i.hasNext()) + { + sr = (SheetRangeImpl) i.next(); + if (sr.getTopLeft().getColumn() == col && + sr.getBottomRight().getColumn() == col) + { + // The column with the merged cells on has been removed, so get + // rid of it from the list + i.remove(); + } + else + { + sr.removeColumn(col); + } + } + } + + /** + * Used to adjust the merged cells following a row removal + */ + void removeRow(int row) + { + SheetRangeImpl sr = null; + Iterator i = ranges.iterator(); + while (i.hasNext()) + { + sr = (SheetRangeImpl) i.next(); + if (sr.getTopLeft().getRow() == row && + sr.getBottomRight().getRow() == row) + { + // The row with the merged cells on has been removed, so get + // rid of it from the list + i.remove(); + } + else + { + sr.removeRow(row); + } + } + } + + /** + * Gets the cells which have been merged on this sheet + * + * @return an array of range objects + */ + Range[] getMergedCells() + { + Range[] cells = new Range[ranges.size()]; + + for (int i=0; i < cells.length; i++) + { + cells[i] = (Range) ranges.get(i); + } + + return cells; + } + + /** + * Unmerges the specified cells. The Range passed in should be one that + * has been previously returned as a result of the getMergedCells method + * + * @param r the range of cells to unmerge + */ + void unmergeCells(Range r) + { + int index = ranges.indexOf(r); + + if (index != -1) + { + ranges.remove(index); + } + } + + /** + * Called prior to writing out in order to check for intersections + */ + private void checkIntersections() + { + ArrayList newcells = new ArrayList(ranges.size()); + + for (Iterator mci = ranges.iterator(); mci.hasNext() ; ) + { + SheetRangeImpl r = (SheetRangeImpl) mci.next(); + + // Check that the range doesn't intersect with any existing range + Iterator i = newcells.iterator(); + SheetRangeImpl range = null; + boolean intersects = false; + while (i.hasNext() && !intersects) + { + range = (SheetRangeImpl) i.next(); + + if (range.intersects(r)) + { + logger.warn("Could not merge cells " + r + + " as they clash with an existing set of merged cells."); + + intersects = true; + } + } + + if (!intersects) + { + newcells.add(r); + } + } + + ranges = newcells; + } + + /** + * Checks the cell ranges for intersections, or if the merged cells + * contains more than one item of data + */ + private void checkRanges() + { + try + { + SheetRangeImpl range = null; + + // Check all the ranges to make sure they only contain one entry + for (int i = 0; i < ranges.size(); i++) + { + range = (SheetRangeImpl) ranges.get(i); + + // Get the cell in the top left + Cell tl = range.getTopLeft(); + Cell br = range.getBottomRight(); + boolean found = false; + + for (int c = tl.getColumn(); c <= br.getColumn(); c++) + { + for (int r = tl.getRow(); r <= br.getRow(); r++) + { + Cell cell = sheet.getCell(c, r); + if (cell.getType() != CellType.EMPTY) + { + if (!found) + { + found = true; + } + else + { + logger.warn("Range " + range + + " contains more than one data cell. " + + "Setting the other cells to blank."); + Blank b = new Blank(c, r); + sheet.addCell(b); + } + } + } + } + } + } + catch (WriteException e) + { + // This should already have been checked - bomb out + Assert.verify(false); + } + } + + void write(File outputFile) throws IOException + { + if (ranges.size() == 0) + { + return; + } + + WorkbookSettings ws = + ( (WritableSheetImpl) sheet).getWorkbookSettings(); + + if (!ws.getMergedCellCheckingDisabled()) + { + checkIntersections(); + checkRanges(); + } + + // If they will all fit into one record, then create a single + // record, write them and get out + if (ranges.size() < maxRangesPerSheet) + { + MergedCellsRecord mcr = new MergedCellsRecord(ranges); + outputFile.write(mcr); + return; + } + + int numRecordsRequired = ranges.size() / maxRangesPerSheet + 1; + int pos = 0; + + for (int i = 0 ; i < numRecordsRequired ; i++) + { + int numranges = Math.min(maxRangesPerSheet, ranges.size() - pos); + + ArrayList cells = new ArrayList(numranges); + for (int j = 0 ; j < numranges ; j++) + { + cells.add(ranges.get(pos+j)); + } + + MergedCellsRecord mcr = new MergedCellsRecord(cells); + outputFile.write(mcr); + + pos += numranges; + } + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/MergedCellsRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/MergedCellsRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/MergedCellsRecord.java 17 Aug 2012 14:51:11 -0000 1.1 @@ -0,0 +1,93 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import java.util.ArrayList; + +import jxl.Cell; +import jxl.Range; +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * A number record. This is stored as 8 bytes, as opposed to the + * 4 byte RK record + */ +public class MergedCellsRecord extends WritableRecordData +{ + /** + * The ranges of all the cells which are merged on this sheet + */ + private ArrayList ranges; + + /** + * Constructs a merged cell record + * + * @param ws the sheet containing the merged cells + */ + protected MergedCellsRecord(ArrayList mc) + { + super(Type.MERGEDCELLS); + + ranges = mc; + } + + /** + * Gets the raw data for output to file + * + * @return the data to write to file + */ + public byte[] getData() + { + byte[] data = new byte[ranges.size() * 8 + 2]; + + // Set the number of ranges + IntegerHelper.getTwoBytes(ranges.size(), data, 0); + + int pos = 2; + Range range = null; + for (int i = 0; i < ranges.size() ; i++) + { + range = (Range) ranges.get(i); + + // Set the various cell records + Cell tl = range.getTopLeft(); + Cell br = range.getBottomRight(); + + IntegerHelper.getTwoBytes(tl.getRow(), data, pos); + IntegerHelper.getTwoBytes(br.getRow(), data, pos+2); + IntegerHelper.getTwoBytes(tl.getColumn(), data, pos+4); + IntegerHelper.getTwoBytes(br.getColumn(), data, pos+6); + + pos += 8; + } + + return data; + } + +} + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/MulRKRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/MulRKRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/MulRKRecord.java 17 Aug 2012 14:51:10 -0000 1.1 @@ -0,0 +1,120 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import java.util.List; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; +import jxl.write.Number; + +/** + * Contains an array of RK numbers + */ +class MulRKRecord extends WritableRecordData +{ + /** + * The row containing these numbers + */ + private int row; + /** + * The first column these rk number occur on + */ + private int colFirst; + /** + * The last column these rk number occur on + */ + private int colLast; + /** + * The array of rk numbers + */ + private int[] rknumbers; + /** + * The array of xf indices + */ + private int[] xfIndices; + + /** + * Constructs the rk numbers from the integer cells + * + * @param numbers A list of jxl.write.Number objects + */ + public MulRKRecord(List numbers) + { + super(Type.MULRK); + row = ((Number)numbers.get(0)).getRow(); + colFirst = ((Number)numbers.get(0)).getColumn(); + colLast = colFirst + numbers.size() - 1; + + rknumbers = new int[numbers.size()]; + xfIndices = new int[numbers.size()]; + + for (int i = 0; i < numbers.size(); i++) + { + rknumbers[i] = (int) ((Number)numbers.get(i)).getValue(); + xfIndices[i] = ( (CellValue) numbers.get(i)).getXFIndex(); + } + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + byte[] data = new byte[rknumbers.length * 6 + 6]; + + // Set up the row and the first column + IntegerHelper.getTwoBytes(row, data, 0); + IntegerHelper.getTwoBytes(colFirst, data, 2); + + // Add all the rk numbers + int pos = 4; + int rkValue = 0; + byte[] rkBytes = new byte[4]; + for (int i = 0; i < rknumbers.length; i++) + { + IntegerHelper.getTwoBytes(xfIndices[i], data, pos); + + // To represent an int as an Excel RK value, we have to + // undergo some outrageous jiggery pokery, as follows: + + // Gets the bit representation of the number + rkValue = rknumbers[i] << 2; + + // Set the integer bit + rkValue |= 0x2; + IntegerHelper.getFourBytes(rkValue, data, pos+2); + + pos+=6; + } + + // Write the number of rk numbers in this record + IntegerHelper.getTwoBytes(colLast, data, pos); + + return data; + } +} + + + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/NameRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/NameRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/NameRecord.java 17 Aug 2012 14:51:12 -0000 1.1 @@ -0,0 +1,655 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import common.Logger; + +import jxl.biff.BuiltInName; +import jxl.biff.IntegerHelper; +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * A name record. Simply takes the binary data from the name + * record read in + */ +class NameRecord extends WritableRecordData +{ + // The logger + private static Logger logger = Logger.getLogger(NameRecord.class); + /** + * The binary data for output to file + */ + private byte[] data; + + /** + * The name + */ + private String name; + + /** + * The built in name + */ + private BuiltInName builtInName; + + /** + * The index into the name table + */ + private int index; + + /** + * The 0-based index sheet reference for a record name + * 0 is for a global reference + */ + private int sheetRef = 0; + + /** + * Modified flag + */ + private boolean modified; + + /** + * A nested class to hold range information + */ + static class NameRange + { + private int columnFirst; + private int rowFirst; + private int columnLast; + private int rowLast; + private int externalSheet; + + NameRange(jxl.read.biff.NameRecord.NameRange nr) + { + columnFirst = nr.getFirstColumn(); + rowFirst = nr.getFirstRow(); + columnLast = nr.getLastColumn(); + rowLast = nr.getLastRow(); + externalSheet = nr.getExternalSheet(); + } + + /** + * Create a new range for the name record. + */ + NameRange(int extSheet, + int theStartRow, + int theEndRow, + int theStartCol, + int theEndCol) + { + columnFirst = theStartCol; + rowFirst = theStartRow; + columnLast = theEndCol; + rowLast = theEndRow; + externalSheet = extSheet; + } + + int getFirstColumn() {return columnFirst;} + int getFirstRow() {return rowFirst;} + int getLastColumn() {return columnLast;} + int getLastRow() {return rowLast;} + int getExternalSheet() { return externalSheet;} + + void incrementFirstRow() { rowFirst++ ; } + void incrementLastRow() { rowLast++ ; } + void decrementFirstRow() { rowFirst-- ; } + void decrementLastRow() { rowLast-- ; } + void incrementFirstColumn() { columnFirst++ ; } + void incrementLastColumn() { columnLast++ ; } + void decrementFirstColumn() { columnFirst-- ; } + void decrementLastColumn() { columnLast-- ; } + + byte[] getData() + { + byte[] d = new byte[10]; + + // Sheet index + IntegerHelper.getTwoBytes(externalSheet, d, 0); + + // Starting row + IntegerHelper.getTwoBytes(rowFirst, d, 2); + + // End row + IntegerHelper.getTwoBytes(rowLast, d, 4); + + // Start column + IntegerHelper.getTwoBytes(columnFirst & 0xff, d, 6); + + // End columns + IntegerHelper.getTwoBytes(columnLast & 0xff, d, 8); + + return d; + } + } + + /** + * The ranges covered by this name + */ + private NameRange[] ranges; + + // Constants which refer to the parse tokens after the string + private static final int cellReference = 0x3a; + private static final int areaReference = 0x3b; + private static final int subExpression = 0x29; + private static final int union = 0x10; + + // An empty range + private static final NameRange EMPTY_RANGE = new NameRange(0,0,0,0,0); + + /** + * Constructor - used when copying sheets + * + * @param index the index into the name table + */ + public NameRecord(jxl.read.biff.NameRecord sr, int ind) + { + super(Type.NAME); + + data = sr.getData(); + name = sr.getName(); + sheetRef = sr.getSheetRef(); + index = ind; + modified = false; + + // Copy the ranges + jxl.read.biff.NameRecord.NameRange[] r = sr.getRanges(); + ranges = new NameRange[r.length]; + for (int i = 0 ; i < ranges.length ; i++) + { + ranges[i] = new NameRange(r[i]); + } + } + + /** + * Create a new name record with the given information. + * + * @param theName Name to be created. + * @param theIndex Index of this name. + * @param extSheet External sheet index this name refers to. + * @param theStartRow First row this name refers to. + * @param theEndRow Last row this name refers to. + * @param theStartCol First column this name refers to. + * @param theEndCol Last column this name refers to. + * @param global TRUE if this is a global name + */ + NameRecord(String theName, + int theIndex, + int extSheet, + int theStartRow, + int theEndRow, + int theStartCol, + int theEndCol, + boolean global) + { + super(Type.NAME); + + name = theName; + index = theIndex; + sheetRef = global ? 0 : index+1; // 0 indicates a global name, otherwise + // the 1-based index of the sheet + + ranges = new NameRange[1]; + ranges[0] = new NameRange(extSheet, + theStartRow, + theEndRow, + theStartCol, + theEndCol); + modified = true; + } + + /** + * Create a new name record with the given information. + * + * @param theName Name to be created. + * @param theIndex Index of this name. + * @param extSheet External sheet index this name refers to. + * @param theStartRow First row this name refers to. + * @param theEndRow Last row this name refers to. + * @param theStartCol First column this name refers to. + * @param theEndCol Last column this name refers to. + * @param global TRUE if this is a global name + */ + NameRecord(BuiltInName theName, + int theIndex, + int extSheet, + int theStartRow, + int theEndRow, + int theStartCol, + int theEndCol, + boolean global) + { + super(Type.NAME); + + builtInName = theName; + index = theIndex; + sheetRef = global ? 0 : index + 1; // 0 indicates a global name, otherwise + // the 1-based index of the sheet + + ranges = new NameRange[1]; + ranges[0] = new NameRange(extSheet, + theStartRow, + theEndRow, + theStartCol, + theEndCol); + } + + /** + * Create a new name record with the given information for 2-range entities. + * + * @param theName Name to be created. + * @param theIndex Index of this name. + * @param extSheet External sheet index this name refers to. + * @param theStartRow First row this name refers to. + * @param theEndRow Last row this name refers to. + * @param theStartCol First column this name refers to. + * @param theEndCol Last column this name refers to. + * @param theStartRow2 First row this name refers to (2nd instance). + * @param theEndRow2 Last row this name refers to (2nd instance). + * @param theStartCol2 First column this name refers to (2nd instance). + * @param theEndCol2 Last column this name refers to (2nd instance). + * @param global TRUE if this is a global name + */ + NameRecord(BuiltInName theName, + int theIndex, + int extSheet, + int theStartRow, + int theEndRow, + int theStartCol, + int theEndCol, + int theStartRow2, + int theEndRow2, + int theStartCol2, + int theEndCol2, + boolean global) + { + super(Type.NAME); + + builtInName = theName; + index = theIndex; + sheetRef = global ? 0 : index + 1; // 0 indicates a global name, otherwise + // the 1-based index of the sheet + + ranges = new NameRange[2]; + ranges[0] = new NameRange(extSheet, + theStartRow, + theEndRow, + theStartCol, + theEndCol); + ranges[1] = new NameRange(extSheet, + theStartRow2, + theEndRow2, + theStartCol2, + theEndCol2); + } + + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + if (data != null && !modified) + { + // this is a copy + return data; + } + + final int NAME_HEADER_LENGTH = 15; + final byte AREA_RANGE_LENGTH = 11; + final byte AREA_REFERENCE = 0x3b; + + int detailLength; + + if (ranges.length > 1) + { + detailLength = (ranges.length * AREA_RANGE_LENGTH) + 4; + } + else + { + detailLength = AREA_RANGE_LENGTH; + } + + int length = NAME_HEADER_LENGTH + detailLength; + length += builtInName != null ? 1 : name.length(); + data = new byte[length]; + + // Options + int options = 0; + + if (builtInName != null) + { + options |= 0x20; + } + IntegerHelper.getTwoBytes(options, data, 0); + + // Keyboard shortcut + data[2] = 0; + + // Length of the name in chars + if (builtInName != null) + { + data[3] = (byte) 0x1; + } + else + { + data[3] = (byte) name.length(); + } + + // Size of the definitions + IntegerHelper.getTwoBytes(detailLength, data, 4); + + // Sheet index + IntegerHelper.getTwoBytes(sheetRef, data, 6); + IntegerHelper.getTwoBytes(sheetRef, data, 8); + + // Byte 10-13 are optional lengths [0,0,0,0] + // Byte 14 is length of name which is not used. + + // The name + if (builtInName != null) + { + data[15] = (byte) builtInName.getValue(); + } + else + { + StringHelper.getBytes(name, data, 15); + } + + // The actual range definition. + int pos = builtInName != null ? 16 : name.length() + 15; + + // If there are multiple ranges for the name, we must specify a + // subExpression type rather than areaReference and then put out + // multiple areaReference entries with an end byte. + if (ranges.length > 1) + { + data[pos++] = subExpression; + // Length of remaining bytes excluding itself + IntegerHelper.getTwoBytes(detailLength - 3, data, pos); + pos += 2; + byte[] rd; + for (int i = 0 ; i < ranges.length ; i++) + { + data[pos++] = areaReference; + rd = ranges[i].getData(); + System.arraycopy(rd, 0, data, pos, rd.length); + pos += rd.length; + } + data[pos] = 0x10; + } + else + { + // Range format - area + data[pos] = areaReference; + + // The range data + byte[] rd = ranges[0].getData(); + System.arraycopy(rd, 0, data, pos+1, rd.length); + } + + return data; + } + + /** + * Accessor for the name + * + * @return the name + */ + public String getName() + { + return name; + } + + /** + * Accessor for the index of this name in the name table + * + * @return the index of this name in the name table + */ + public int getIndex() + { + return index; + } + + /** + * The 0-based index sheet reference for a record name + * 0 is for a global reference + * + * @return the sheet reference for name formula + */ + public int getSheetRef() + { + return sheetRef; + } + + /** + * Set the index sheet reference for a record name + * 0 is for a global reference + * + */ + public void setSheetRef(int i) + { + sheetRef = i; + IntegerHelper.getTwoBytes(sheetRef, data, 8); + } + + /** + * Gets the array of ranges for this name + * @return the ranges + */ + public NameRange[] getRanges() + { + return ranges; + } + + /** + * Called when a row is inserted on the + * + * @param sheetIndex the sheet index on which the column was inserted + * @param row the column number which was inserted + */ + void rowInserted(int sheetIndex, int row) + { + for (int i = 0 ; i < ranges.length ; i++) + { + if (sheetIndex != ranges[i].getExternalSheet()) + { + continue; // shame on me - this is no better than a goto + } + + if (row <= ranges[i].getFirstRow()) + { + ranges[i].incrementFirstRow(); + modified = true; + } + + if (row <= ranges[i].getLastRow()) + { + ranges[i].incrementLastRow(); + modified = true; + } + } + } + + /** + * Called when a row is removed on the worksheet + * + * @param sheetIndex the sheet index on which the column was inserted + * @param row the column number which was inserted + * @reeturn TRUE if the name is to be removed entirely, FALSE otherwise + */ + boolean rowRemoved(int sheetIndex, int row) + { + for (int i = 0 ; i < ranges.length ; i++) + { + if (sheetIndex != ranges[i].getExternalSheet()) + { + continue; // shame on me - this is no better than a goto + } + + if (row == ranges[i].getFirstRow() && row == ranges[i].getLastRow()) + { + // remove the range + ranges[i] = EMPTY_RANGE; + } + + if (row < ranges[i].getFirstRow() && row > 0) + { + ranges[i].decrementFirstRow(); + modified = true; + } + + if (row <= ranges[i].getLastRow()) + { + ranges[i].decrementLastRow(); + modified = true; + } + } + + // If all ranges are empty, then remove the name + int emptyRanges = 0; + for (int i = 0 ; i < ranges.length; i++) + { + if (ranges[i] == EMPTY_RANGE) + { + emptyRanges++; + } + } + + if (emptyRanges == ranges.length) + { + return true; + } + + // otherwise just remove the empty ones + NameRange[] newRanges = new NameRange[ranges.length - emptyRanges]; + for (int i = 0 ; i < ranges.length ; i++) + { + if (ranges[i] != EMPTY_RANGE) + { + newRanges[i] = ranges[i]; + } + } + + ranges = newRanges; + + return false; + } + + /** + * Called when a row is removed on the worksheet + * + * @param sheetIndex the sheet index on which the column was inserted + * @param row the column number which was inserted + * @reeturn TRUE if the name is to be removed entirely, FALSE otherwise + */ + boolean columnRemoved(int sheetIndex, int col) + { + for (int i = 0 ; i < ranges.length ; i++) + { + if (sheetIndex != ranges[i].getExternalSheet()) + { + continue; // shame on me - this is no better than a goto + } + + if (col == ranges[i].getFirstColumn() && col == + ranges[i].getLastColumn()) + { + // remove the range + ranges[i] = EMPTY_RANGE; + } + + if (col < ranges[i].getFirstColumn() && col > 0) + { + ranges[i].decrementFirstColumn(); + modified = true; + } + + if (col <= ranges[i].getLastColumn()) + { + ranges[i].decrementLastColumn(); + modified = true; + } + } + + // If all ranges are empty, then remove the name + int emptyRanges = 0; + for (int i = 0 ; i < ranges.length; i++) + { + if (ranges[i] == EMPTY_RANGE) + { + emptyRanges++; + } + } + + if (emptyRanges == ranges.length) + { + return true; + } + + // otherwise just remove the empty ones + NameRange[] newRanges = new NameRange[ranges.length - emptyRanges]; + for (int i = 0 ; i < ranges.length ; i++) + { + if (ranges[i] != EMPTY_RANGE) + { + newRanges[i] = ranges[i]; + } + } + + ranges = newRanges; + + return false; + } + + + /** + * Called when a row is inserted on the + * + * @param sheetIndex the sheet index on which the column was inserted + * @param col the column number which was inserted + */ + void columnInserted(int sheetIndex, int col) + { + for (int i = 0 ; i < ranges.length ; i++) + { + if (sheetIndex != ranges[i].getExternalSheet()) + { + continue; // shame on me - this is no better than a goto + } + + if (col <= ranges[i].getFirstColumn()) + { + ranges[i].incrementFirstColumn(); + modified = true; + } + + if (col <= ranges[i].getLastColumn()) + { + ranges[i].incrementLastColumn(); + modified = true; + } + } + } + +} + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/NineteenFourRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/NineteenFourRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/NineteenFourRecord.java 17 Aug 2012 14:51:10 -0000 1.1 @@ -0,0 +1,68 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * A record which indicates whether or the 1904 date system is + * in use + */ +class NineteenFourRecord extends WritableRecordData +{ + /** + * Flag which indicates whether the 1904 date system is being used + */ + private boolean nineteenFourDate; + + /** + * The binary data for output to file + */ + private byte[] data; + + /** + * Constructor + * + * @param oldDate flag indicating whether the 1904 date system is in use + */ + public NineteenFourRecord(boolean oldDate) + { + super(Type.NINETEENFOUR); + + nineteenFourDate = oldDate; + data = new byte[2]; + + if (nineteenFourDate) + { + IntegerHelper.getTwoBytes(1, data, 0); + } + } + + /** + * The binary data for output to file + * @return the binary data + */ + public byte[] getData() + { + return data; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/NumberFormatRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/NumberFormatRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/NumberFormatRecord.java 17 Aug 2012 14:51:09 -0000 1.1 @@ -0,0 +1,137 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import common.Logger; + +import jxl.biff.FormatRecord; + +/** + * A class which contains a number format + */ +public class NumberFormatRecord extends FormatRecord +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(NumberFormatRecord.class); + + // Dummy class to specify non validation + protected static class NonValidatingFormat{public NonValidatingFormat(){}}; + + + /** + * Constructor. Replaces some of the characters in the java number + * format string with the appropriate excel format characters + * + * @param fmt the number format + */ + protected NumberFormatRecord(String fmt) + { + super(); + + // Do the replacements in the format string + String fs = fmt; + + fs = replace(fs, "E0", "E+0"); + + fs = trimInvalidChars(fs); + + setFormatString(fs); + } + + /** + * Constructor. Replaces some of the characters in the java number + * format string with the appropriate excel format characters + * + * @param fmt the number format + */ + protected NumberFormatRecord(String fmt, NonValidatingFormat dummy) + { + super(); + + // Do the replacements in the format string + String fs = fmt; + + fs = replace(fs, "E0", "E+0"); + + setFormatString(fs); + } + + /** + * Remove all but the first characters preceding the # or the 0. + * Remove all characters after the # or the 0, unless it is a ) + * + * @param fs the candidate number format + * @return the string with spurious characters removed + */ + private String trimInvalidChars(String fs) + { + int firstHash = fs.indexOf('#'); + int firstZero = fs.indexOf('0'); + int firstValidChar = 0; + + if (firstHash == -1 && firstZero == -1) + { + // The string is complete nonsense. Return a default string + return "#.###"; + } + + if (firstHash != 0 && firstZero != 0 && + firstHash != 1 && firstZero != 1) + { + // The string is dodgy. Find the first valid char + firstHash = firstHash == -1?firstHash = Integer.MAX_VALUE:firstHash; + firstZero = firstZero == -1?firstZero = Integer.MAX_VALUE:firstZero; + firstValidChar = Math.min(firstHash, firstZero); + + StringBuffer tmp = new StringBuffer(); + tmp.append(fs.charAt(0)); + tmp.append(fs.substring(firstValidChar)); + fs = tmp.toString(); + } + + // Now strip of everything at the end that isn't a # or 0 + int lastHash = fs.lastIndexOf('#'); + int lastZero = fs.lastIndexOf('0'); + + if (lastHash == fs.length() || + lastZero == fs.length()) + { + return fs; + } + + // Find the last valid character + int lastValidChar = Math.max(lastHash, lastZero); + + // Check for the existence of a ) or % + while ((fs.length() > lastValidChar + 1) && + (fs.charAt(lastValidChar+1) == ')' || + (fs.charAt(lastValidChar+1) == '%'))) + { + lastValidChar++; + } + + return fs.substring(0, lastValidChar+1); + } +} + + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/NumberRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/NumberRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/NumberRecord.java 17 Aug 2012 14:51:11 -0000 1.1 @@ -0,0 +1,183 @@ +/********************************************************************* +* +* Copyright (C) 2001 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import java.text.DecimalFormat; +import java.text.NumberFormat; + +import jxl.CellType; +import jxl.NumberCell; +import jxl.biff.DoubleHelper; +import jxl.biff.Type; +import jxl.biff.XFRecord; +import jxl.format.CellFormat; + +/** + * The record which contains numerical values. All values are stored + * as 64bit IEEE floating point values + */ +public abstract class NumberRecord extends CellValue +{ + /** + * The number + */ + private double value; + + /** + * The java equivalent of the excel format + */ + private NumberFormat format; + + /** + * The formatter to convert the value into a string + */ + private static DecimalFormat defaultFormat = new DecimalFormat("#.###"); + + /** + * Constructor invoked by the user API + * + * @param c the column + * @param r the row + * @param val the value + */ + protected NumberRecord(int c, int r, double val) + { + super(Type.NUMBER, c, r); + value = val; + } + + /** + * Overloaded constructor invoked from the API, which takes a cell + * format + * + * @param c the column + * @param r the row + * @param val the value + * @param st the cell format + */ + protected NumberRecord(int c, int r, double val, CellFormat st) + { + super(Type.NUMBER, c, r, st); + value = val; + } + + /** + * Constructor used when copying a workbook + * + * @param nc the number to copy + */ + protected NumberRecord(NumberCell nc) + { + super(Type.NUMBER, nc); + value = nc.getValue(); + } + + /** + * Copy constructor + * + * @param c the column + * @param r the row + * @param nr the record to copy + */ + protected NumberRecord(int c, int r, NumberRecord nr) + { + super(Type.NUMBER, c, r, nr); + value = nr.value; + } + + /** + * Returns the content type of this cell + * + * @return the content type for this cell + */ + public CellType getType() + { + return CellType.NUMBER; + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + byte[] celldata = super.getData(); + byte[] data = new byte[celldata.length + 8]; + System.arraycopy(celldata, 0, data, 0, celldata.length); + DoubleHelper.getIEEEBytes(value, data, celldata.length); + + return data; + } + + /** + * Quick and dirty function to return the contents of this cell as a string. + * For more complex manipulation of the contents, it is necessary to cast + * this interface to correct subinterface + * + * @return the contents of this cell as a string + */ + public String getContents() + { + if (format == null) + { + format = ( (XFRecord) getCellFormat()).getNumberFormat(); + if (format == null) + { + format = defaultFormat; + } + } + return format.format(value); + } + + /** + * Gets the double contents for this cell. + * + * @return the cell contents + */ + public double getValue() + { + return value; + } + + /** + * Sets the value of the contents for this cell + * + * @param val the new value + */ + public void setValue(double val) + { + value = val; + } + + /** + * Gets the NumberFormat used to format this cell. This is the java + * equivalent of the Excel format + * + * @return the NumberFormat used to format the cell + */ + public NumberFormat getNumberFormat() + { + return null; + } +} + + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/ObjProjRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/ObjProjRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/ObjProjRecord.java 17 Aug 2012 14:51:09 -0000 1.1 @@ -0,0 +1,62 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record containing the obj proj record + */ +class ObjProjRecord extends WritableRecordData +{ + /** + * The binary data + */ + private byte[] data; + + /** + * Constructor + */ + public ObjProjRecord() + { + super(Type.OBJPROJ); + + data = new byte[4]; + } + + /** + * Retrieves the data to be written to the binary file + * + * @return the binary data + */ + public byte[] getData() + { + return data; + } +} + + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/ObjectProtectRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/ObjectProtectRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/ObjectProtectRecord.java 17 Aug 2012 14:51:09 -0000 1.1 @@ -0,0 +1,68 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * The protection state for a sheet or workbook + */ +class ObjectProtectRecord extends WritableRecordData +{ + /** + * The protection state + */ + private boolean protection; + /** + * The binary data + */ + private byte[] data; + + /** + * Constructor + * + * @param prot the protection state + */ + public ObjectProtectRecord(boolean prot) + { + super(Type.OBJPROTECT); + + protection = prot; + + data = new byte[2]; + + if (protection) + { + IntegerHelper.getTwoBytes(1, data, 0); + } + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + return data; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/PLSRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/PLSRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/PLSRecord.java 17 Aug 2012 14:51:12 -0000 1.1 @@ -0,0 +1,71 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which specifies a print header for a work sheet + */ +class PLSRecord extends WritableRecordData +{ + /** + * The binary data + */ + private byte[] data; + + /** + * Consructor invoked when copying a spreadsheet + * + * @param hr the read header record + */ + public PLSRecord(jxl.read.biff.PLSRecord hr) + { + super(Type.PLS); + + data = hr.getData(); + } + + /** + * Consructor invoked when copying a sheets + * + * @param hr the read header record + */ + public PLSRecord(PLSRecord hr) + { + super(Type.PLS); + + data = new byte[hr.data.length]; + System.arraycopy(hr.data, 0, data, 0, data.length); + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + return data; + } +} + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/PaletteRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/PaletteRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/PaletteRecord.java 17 Aug 2012 14:51:11 -0000 1.1 @@ -0,0 +1,56 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Stores the colour palette + */ +class PaletteRecord extends WritableRecordData +{ + /** + * The binary data + */ + private byte[] data; + + /** + * Constructor + * + * @param p the palette record + */ + public PaletteRecord(jxl.read.biff.PaletteRecord p) + { + super(Type.PALETTE); + + data = p.getData(); + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + return data; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/PaneRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/PaneRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/PaneRecord.java 17 Aug 2012 14:51:12 -0000 1.1 @@ -0,0 +1,109 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Contains the window attributes for a worksheet + */ +class PaneRecord extends WritableRecordData +{ + /** + * The number of rows visible in the top left pane + */ + private int rowsVisible; + /** + * The number of columns visible in the top left pane + */ + private int columnsVisible; + + /** + * The pane codes + */ + private final static int topLeftPane = 0x3; + private final static int bottomLeftPane = 0x2; + private final static int topRightPane = 0x1; + private final static int bottomRightPane = 0x0; + + /** + * Code + + /** + * Constructor + */ + public PaneRecord(int cols, int rows) + { + super(Type.PANE); + + rowsVisible = rows; + columnsVisible = cols; + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + byte[] data = new byte[10]; + + // The x position + IntegerHelper.getTwoBytes(columnsVisible, data, 0); + + // The y position + IntegerHelper.getTwoBytes(rowsVisible, data, 2); + + // The top row visible in the bottom pane + if (rowsVisible > 0) + { + IntegerHelper.getTwoBytes(rowsVisible, data, 4); + } + + // The left most column visible in the right pane + if (columnsVisible > 0) + { + IntegerHelper.getTwoBytes(columnsVisible, data, 6); + } + + // The active pane + int activePane = topLeftPane; + + if (rowsVisible > 0 && columnsVisible == 0) + { + activePane = bottomLeftPane; + } + else if (rowsVisible == 0 && columnsVisible > 0) + { + activePane = topRightPane; + } + else if (rowsVisible > 0 && columnsVisible > 0) + { + activePane = bottomRightPane; + } + // always present + IntegerHelper.getTwoBytes(activePane, data, 8); + + return data; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/PasswordRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/PasswordRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/PasswordRecord.java 17 Aug 2012 14:51:09 -0000 1.1 @@ -0,0 +1,122 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * A password record. Thanks to Michael Matthews for sending me the + * code to actually store the password for the sheet + */ +class PasswordRecord extends WritableRecordData +{ + /** + * The password + */ + private String password; + /** + * The binary data + */ + private byte[] data; + + /** + * Constructor + * + * @param pw the password + */ + public PasswordRecord(String pw) + { + super(Type.PASSWORD); + + password = pw; + + if (pw == null) + { + data = new byte[2]; + IntegerHelper.getTwoBytes(0, data, 0); + } + else + { + byte [] passwordBytes = pw.getBytes(); + int passwordHash = 0; + for (int a = 0; a < passwordBytes.length; a++) + { + int shifted = rotLeft15Bit(passwordBytes[a], a + 1); + passwordHash ^= shifted; + } + passwordHash ^= passwordBytes.length; + passwordHash ^= 0xCE4B; + + data = new byte[2]; + IntegerHelper.getTwoBytes(passwordHash, data, 0); + } + } + + /** + * Constructor + * + * @param ph the password hash code + */ + public PasswordRecord(int ph) + { + super(Type.PASSWORD); + + data = new byte[2]; + IntegerHelper.getTwoBytes(ph, data, 0); + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + return data; + } + + /** + * Rotate the value by 15 bits. Thanks to Michael for this + * + * @param val + * @param rotate + * @return int + */ + private int rotLeft15Bit(int val, int rotate) + { + val = val &0x7FFF; + + for(; rotate > 0; rotate--) + { + if((val & 0x4000) != 0) + { + val = ((val << 1) & 0x7FFF) + 1; + } + else + { + val = (val << 1) & 0x7FFF; + } + } + + return val; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/PrecisionRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/PrecisionRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/PrecisionRecord.java 17 Aug 2012 14:51:11 -0000 1.1 @@ -0,0 +1,67 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Stores the Precision As Displayed option from the dialog box + */ +class PrecisionRecord extends WritableRecordData +{ + /** + * The precision as displayed flag + */ + private boolean asDisplayed; + /** + * The binary data + */ + private byte[] data; + + /** + * Constructor + * + * @param disp the precision as displayed flag + */ + public PrecisionRecord(boolean disp) + { + super(Type.PRECISION); + + asDisplayed = disp; + data = new byte[2]; + + if (!asDisplayed) + { + IntegerHelper.getTwoBytes(1, data, 0); + } + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + return data; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/PrintGridLinesRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/PrintGridLinesRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/PrintGridLinesRecord.java 17 Aug 2012 14:51:10 -0000 1.1 @@ -0,0 +1,68 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * The grid lines option from the Page Setup dialog box + */ +class PrintGridLinesRecord extends WritableRecordData +{ + /** + * The binary data + */ + private byte[] data; + /** + * The print grid lines option + */ + private boolean printGridLines; + + /** + * Constructor + * + * @param pgl the grid lines option + */ + public PrintGridLinesRecord(boolean pgl) + { + super(Type.PRINTGRIDLINES); + printGridLines = pgl; + + data = new byte[2]; + + if (printGridLines) + { + data[0] = 1; + } + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + return data; + } +} + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/PrintHeadersRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/PrintHeadersRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/PrintHeadersRecord.java 17 Aug 2012 14:51:11 -0000 1.1 @@ -0,0 +1,68 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * The headings options from the Page Setup dialog box + */ +class PrintHeadersRecord extends WritableRecordData +{ + /** + * The binary data + */ + private byte[] data; + /** + * Flag to print headers + */ + private boolean printHeaders; + + /** + * Constructor + * + * @param ph print headers flag + */ + public PrintHeadersRecord(boolean ph) + { + super(Type.PRINTHEADERS); + printHeaders = ph; + + data = new byte[2]; + + if (printHeaders) + { + data[0] = 1; + } + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + return data; + } +} + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/Prot4RevPassRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/Prot4RevPassRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/Prot4RevPassRecord.java 17 Aug 2012 14:51:09 -0000 1.1 @@ -0,0 +1,55 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Stores the shared workbook protection flag + */ +class Prot4RevPassRecord extends WritableRecordData +{ + /** + * The binary data + */ + private byte[] data; + + /** + * Constructor + */ + public Prot4RevPassRecord() + { + super(Type.PROT4REVPASS); + + // Hard code in an unprotected workbook + data = new byte[2]; + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + return data; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/Prot4RevRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/Prot4RevRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/Prot4RevRecord.java 17 Aug 2012 14:51:11 -0000 1.1 @@ -0,0 +1,69 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * The shared workbook protection flag + */ +class Prot4RevRecord extends WritableRecordData +{ + /** + * The protection flag + */ + private boolean protection; + /** + * The binary data + */ + private byte[] data; + + /** + * Constructor + * + * @param prot protection flag + */ + public Prot4RevRecord(boolean prot) + { + super(Type.PROT4REV); + + protection = prot; + + // Hard code in an unprotected workbook + data = new byte[2]; + + if (protection) + { + IntegerHelper.getTwoBytes(1, data, 0); + } + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + return data; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/ProtectRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/ProtectRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/ProtectRecord.java 17 Aug 2012 14:51:09 -0000 1.1 @@ -0,0 +1,68 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * The protection state for a sheet or workbook + */ +class ProtectRecord extends WritableRecordData +{ + /** + * The protection state + */ + private boolean protection; + /** + * The binary data + */ + private byte[] data; + + /** + * Constructor + * + * @param prot the protection state + */ + public ProtectRecord(boolean prot) + { + super(Type.PROTECT); + + protection = prot; + + data = new byte[2]; + + if (protection) + { + IntegerHelper.getTwoBytes(1, data, 0); + } + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + return data; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/ReadBooleanFormulaRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/ReadBooleanFormulaRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/ReadBooleanFormulaRecord.java 17 Aug 2012 14:51:09 -0000 1.1 @@ -0,0 +1,50 @@ +/********************************************************************* +* +* Copyright (C) 2004 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.BooleanFormulaCell; +import jxl.biff.FormulaData; + +/** + * Class for read number formula records + */ +class ReadBooleanFormulaRecord extends ReadFormulaRecord + implements BooleanFormulaCell +{ + /** + * Constructor + * + * @param f + */ + public ReadBooleanFormulaRecord(FormulaData f) + { + super(f); + } + + /** + * Gets the boolean contents for this cell. + * + * @return the cell contents + */ + public boolean getValue() + { + return ( (BooleanFormulaCell) getReadFormula()).getValue(); + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/ReadDateFormulaRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/ReadDateFormulaRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/ReadDateFormulaRecord.java 17 Aug 2012 14:51:12 -0000 1.1 @@ -0,0 +1,76 @@ +/********************************************************************* +* +* Copyright (C) 2004 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import java.text.DateFormat; +import java.util.Date; + +import jxl.DateFormulaCell; +import jxl.biff.FormulaData; + +/** + * Class for read number formula records + */ +class ReadDateFormulaRecord extends ReadFormulaRecord + implements DateFormulaCell +{ + /** + * Constructor + * + * @param f + */ + public ReadDateFormulaRecord(FormulaData f) + { + super(f); + } + + /** + * Gets the Date contents for this cell. + * + * @return the cell contents + */ + public Date getDate() + { + return ( (DateFormulaCell) getReadFormula()).getDate(); + } + + /** + * Indicates whether the date value contained in this cell refers to a date, + * or merely a time + * + * @return TRUE if the value refers to a time + */ + public boolean isTime() + { + return ( (DateFormulaCell) getReadFormula()).isTime(); + } + + + /** + * Gets the DateFormat used to format this cell. This is the java + * equivalent of the Excel format + * + * @return the DateFormat used to format the cell + */ + public DateFormat getDateFormat() + { + return ( (DateFormulaCell) getReadFormula()).getDateFormat(); + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/ReadErrorFormulaRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/ReadErrorFormulaRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/ReadErrorFormulaRecord.java 17 Aug 2012 14:51:11 -0000 1.1 @@ -0,0 +1,133 @@ +/********************************************************************* +* +* Copyright (C) 2004 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import common.Logger; + +import jxl.ErrorFormulaCell; +import jxl.biff.FormulaData; +import jxl.biff.IntegerHelper; +import jxl.biff.formula.FormulaErrorCode; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; + + +/** + * Class for read number formula records + */ +class ReadErrorFormulaRecord extends ReadFormulaRecord + implements ErrorFormulaCell +{ + // The logger + private static Logger logger = Logger.getLogger(ReadErrorFormulaRecord.class); + + /** + * Constructor + * + * @param f + */ + public ReadErrorFormulaRecord(FormulaData f) + { + super(f); + } + + /** + * Gets the error code for this cell. + * + * @return the cell contents + */ + public int getErrorCode() + { + return ( (ErrorFormulaCell) getReadFormula()).getErrorCode(); + } + + /** + * Error formula specific exception handling. Can't really create + * a formula (as it will look for a cell of that name, so just + * create a STRING record containing the contents + * + * @return the bodged data + */ + protected byte[] handleFormulaException() + { + byte[] expressiondata = null; + byte[] celldata = super.getCellData(); + + int errorCode = getErrorCode(); + String formulaString = null; + + if (errorCode == FormulaErrorCode.DIV0.getCode()) + { + formulaString = "1/0"; + } + else if (errorCode == FormulaErrorCode.VALUE.getCode()) + { + formulaString = "\"\"/0"; + } + else if (errorCode == FormulaErrorCode.REF.getCode()) + { + formulaString = "\"#REF!\""; + } + else + { + formulaString = "\"ERROR\""; + } + + // Generate an appropriate dummy formula + WritableWorkbookImpl w = getSheet().getWorkbook(); + FormulaParser parser = new FormulaParser(formulaString, w, w, + w.getSettings()); + + // Get the bytes for the dummy formula + try + { + parser.parse(); + } + catch(FormulaException e2) + { + logger.warn(e2.getMessage()); + } + byte[] formulaBytes = parser.getBytes(); + expressiondata = new byte[formulaBytes.length + 16]; + IntegerHelper.getTwoBytes(formulaBytes.length, expressiondata, 14); + System.arraycopy(formulaBytes, 0, expressiondata, 16, + formulaBytes.length); + + // Set the recalculate on load bit + expressiondata[8] |= 0x02; + + byte[] data = new byte[celldata.length + + expressiondata.length]; + System.arraycopy(celldata, 0, data, 0, celldata.length); + System.arraycopy(expressiondata, 0, data, + celldata.length, expressiondata.length); + + // Set the type bits to indicate an error + data[6] = 2; + data[12] = -1; + data[13] = -1; + + // Set the error code + data[8] = (byte) errorCode; + + return data; + } + +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/ReadFormulaRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/ReadFormulaRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/ReadFormulaRecord.java 17 Aug 2012 14:51:09 -0000 1.1 @@ -0,0 +1,448 @@ +/************************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import common.Assert; +import common.Logger; + +import jxl.CellReferenceHelper; +import jxl.CellType; +import jxl.FormulaCell; +import jxl.Sheet; +import jxl.WorkbookSettings; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; +import jxl.write.WritableCell; + +/** + * A formula record. This is invoked when copying a formula from a + * read only spreadsheet + * This method implements the FormulaData interface to allow the copying + * of writable sheets + */ +class ReadFormulaRecord extends CellValue implements FormulaData +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(ReadFormulaRecord.class); + + /** + * The underlying formula from the read sheet + */ + private FormulaData formula; + + /** + * The formula parser + */ + private FormulaParser parser; + + /** + * Constructor + * + * @param f the formula to copy + */ + protected ReadFormulaRecord(FormulaData f) + { + super(Type.FORMULA, f); + formula = f; + } + + protected final byte[] getCellData() + { + return super.getData(); + } + + /** + * An exception has occurred, so produce some appropriate dummy + * cell contents. This may be overridden by subclasses + * if they require specific handling + * + * @return the bodged data + */ + protected byte[] handleFormulaException() + { + byte[] expressiondata = null; + byte[] celldata = super.getData(); + + // Generate an appropriate dummy formula + WritableWorkbookImpl w = getSheet().getWorkbook(); + parser = new FormulaParser(getContents(), w, w, + w.getSettings()); + + // Get the bytes for the dummy formula + try + { + parser.parse(); + } + catch(FormulaException e2) + { + logger.warn(e2.getMessage()); + parser = new FormulaParser("\"ERROR\"", w, w, w.getSettings()); + try {parser.parse();} + catch(FormulaException e3) {Assert.verify(false);} + } + byte[] formulaBytes = parser.getBytes(); + expressiondata = new byte[formulaBytes.length + 16]; + IntegerHelper.getTwoBytes(formulaBytes.length, expressiondata, 14); + System.arraycopy(formulaBytes, 0, expressiondata, 16, + formulaBytes.length); + + // Set the recalculate on load bit + expressiondata[8] |= 0x02; + + byte[] data = new byte[celldata.length + + expressiondata.length]; + System.arraycopy(celldata, 0, data, 0, celldata.length); + System.arraycopy(expressiondata, 0, data, + celldata.length, expressiondata.length); + return data; + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + // Take the superclass cell data to take into account cell + // rationalization + byte[] celldata = super.getData(); + byte[] expressiondata = null; + + try + { + if (parser == null) + { + expressiondata = formula.getFormulaData(); + } + else + { + byte[] formulaBytes = parser.getBytes(); + expressiondata = new byte[formulaBytes.length + 16]; + IntegerHelper.getTwoBytes(formulaBytes.length, expressiondata, 14); + System.arraycopy(formulaBytes, 0, expressiondata, 16, + formulaBytes.length); + } + + // Set the recalculate on load bit + expressiondata[8] |= 0x02; + + byte[] data = new byte[celldata.length + + expressiondata.length]; + System.arraycopy(celldata, 0, data, 0, celldata.length); + System.arraycopy(expressiondata, 0, data, + celldata.length, expressiondata.length); + return data; + } + catch (FormulaException e) + { + // Something has gone wrong trying to read the formula data eg. it + // might be unsupported biff7 data + logger.warn + (CellReferenceHelper.getCellReference(getColumn(), getRow()) + + " " + e.getMessage()); + return handleFormulaException(); + } + } + + /** + * Returns the content type of this cell + * + * @return the content type for this cell + */ + public CellType getType() + { + return formula.getType(); + } + + /** + * Quick and dirty function to return the contents of this cell as a string. + * + * @return the contents of this cell as a string + */ + public String getContents() + { + return formula.getContents(); + } + + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array. Used when copying spreadsheets + * + * @return the raw record data + */ + public byte[] getFormulaData() throws FormulaException + { + byte[] d = formula.getFormulaData(); + byte[] data = new byte[d.length]; + + System.arraycopy(d, 0, data, 0, d.length); + + // Set the recalculate on load bit + data[8] |= 0x02; + + return data; + } + + /** + * Gets the formula bytes + * + * @return the formula bytes + */ + public byte[] getFormulaBytes() throws FormulaException + { + // If the formula has been parsed, then get the parsed bytes + if (parser != null) + { + return parser.getBytes(); + } + + // otherwise get the bytes from the original formula + byte[] readFormulaData = getFormulaData(); + byte[] formulaBytes = new byte[readFormulaData.length - 16]; + System.arraycopy(readFormulaData, 16, formulaBytes, 0, + formulaBytes.length); + return formulaBytes; + } + + /** + * Implementation of the deep copy function + * + * @param col the column which the new cell will occupy + * @param row the row which the new cell will occupy + * @return a copy of this cell, which can then be added to the sheet + */ + public WritableCell copyTo(int col, int row) + { + return new FormulaRecord(col, row, this); + } + + /** + * Overrides the method in the base class to add this to the Workbook's + * list of maintained formulas + * + * @param fr the formatting records + * @param ss the shared strings used within the workbook + * @param s the sheet this is being added to + */ + void setCellDetails(FormattingRecords fr, SharedStrings ss, + WritableSheetImpl s) + { + super.setCellDetails(fr, ss, s); + s.getWorkbook().addRCIRCell(this); + } + + /** + * Called when a column is inserted on the specified sheet. Notifies all + * RCIR cells of this change. The default implementation here does nothing + * + * @param s the sheet on which the column was inserted + * @param sheetIndex the sheet index on which the column was inserted + * @param col the column number which was inserted + */ + void columnInserted(Sheet s, int sheetIndex, int col) + { + try + { + if (parser == null) + { + byte[] formulaData = formula.getFormulaData(); + byte[] formulaBytes = new byte[formulaData.length - 16]; + System.arraycopy(formulaData, 16, + formulaBytes, 0, formulaBytes.length); + parser = new FormulaParser(formulaBytes, + this, + getSheet().getWorkbook(), + getSheet().getWorkbook(), + getSheet().getWorkbookSettings()); + parser.parse(); + } + + parser.columnInserted(sheetIndex, col, s == getSheet()); + } + catch (FormulaException e) + { + logger.warn("cannot insert column within formula: " + e.getMessage()); + } + } + + /** + * Called when a column is removed on the specified sheet. Notifies all + * RCIR cells of this change. The default implementation here does nothing + * + * @param s the sheet on which the column was inserted + * @param sheetIndex the sheet index on which the column was inserted + * @param col the column number which was inserted + */ + void columnRemoved(Sheet s, int sheetIndex, int col) + { + try + { + if (parser == null) + { + byte[] formulaData = formula.getFormulaData(); + byte[] formulaBytes = new byte[formulaData.length - 16]; + System.arraycopy(formulaData, 16, + formulaBytes, 0, formulaBytes.length); + parser = new FormulaParser(formulaBytes, + this, + getSheet().getWorkbook(), + getSheet().getWorkbook(), + getSheet().getWorkbookSettings()); + parser.parse(); + } + + parser.columnRemoved(sheetIndex, col, s == getSheet()); + } + catch (FormulaException e) + { + logger.warn("cannot remove column within formula: " + e.getMessage()); + } + } + + /** + * Called when a row is inserted on the specified sheet. Notifies all + * RCIR cells of this change. The default implementation here does nothing + * + * @param s the sheet on which the column was inserted + * @param sheetIndex the sheet index on which the column was inserted + * @param row the column number which was inserted + */ + void rowInserted(Sheet s, int sheetIndex, int row) + { + try + { + if (parser == null) + { + byte[] formulaData = formula.getFormulaData(); + byte[] formulaBytes = new byte[formulaData.length - 16]; + System.arraycopy(formulaData, 16, + formulaBytes, 0, formulaBytes.length); + parser = new FormulaParser(formulaBytes, + this, + getSheet().getWorkbook(), + getSheet().getWorkbook(), + getSheet().getWorkbookSettings()); + parser.parse(); + } + + parser.rowInserted(sheetIndex, row, s == getSheet()); + } + catch (FormulaException e) + { + logger.warn("cannot insert row within formula: " + e.getMessage()); + } + } + + /** + * Called when a row is inserted on the specified sheet. Notifies all + * RCIR cells of this change. The default implementation here does nothing + * + * @param s the sheet on which the row was removed + * @param sheetIndex the sheet index on which the column was removed + * @param row the column number which was removed + */ + void rowRemoved(Sheet s, int sheetIndex, int row) + { + try + { + if (parser == null) + { + byte[] formulaData = formula.getFormulaData(); + byte[] formulaBytes = new byte[formulaData.length - 16]; + System.arraycopy(formulaData, 16, + formulaBytes, 0, formulaBytes.length); + parser = new FormulaParser(formulaBytes, + this, + getSheet().getWorkbook(), + getSheet().getWorkbook(), + getSheet().getWorkbookSettings()); + parser.parse(); + } + + parser.rowRemoved(sheetIndex, row, s == getSheet()); + } + catch (FormulaException e) + { + logger.warn("cannot remove row within formula: " + e.getMessage()); + } + } + + /** + * Accessor for the read formula + * + * @return the read formula + */ + protected FormulaData getReadFormula() + { + return formula; + } + + /** + * Accessor for the read formula + * + * @return the read formula + */ + public String getFormula() throws FormulaException + { + return ( (FormulaCell) formula).getFormula(); + } + + /** + * If this formula was on an imported sheet, check that + * cell references to another sheet are warned appropriately + * + * @return TRUE if this formula was able to be imported, FALSE otherwise + */ + public boolean handleImportedCellReferences(ExternalSheet es, + WorkbookMethods mt, + WorkbookSettings ws) + { + try + { + if (parser == null) + { + byte[] formulaData = formula.getFormulaData(); + byte[] formulaBytes = new byte[formulaData.length - 16]; + System.arraycopy(formulaData, 16, + formulaBytes, 0, formulaBytes.length); + parser = new FormulaParser(formulaBytes, + this, + es, mt, ws); + parser.parse(); + } + + return parser.handleImportedCellReferences(); + } + catch (FormulaException e) + { + logger.warn("cannot import formula: " + e.getMessage()); + return false; + } + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/ReadNumberFormulaRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/ReadNumberFormulaRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/ReadNumberFormulaRecord.java 17 Aug 2012 14:51:13 -0000 1.1 @@ -0,0 +1,120 @@ +/********************************************************************* +* +* Copyright (C) 2004 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import java.text.NumberFormat; + +import common.Logger; + +import jxl.NumberFormulaCell; +import jxl.biff.DoubleHelper; +import jxl.biff.FormulaData; +import jxl.biff.IntegerHelper; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; + +/** + * Class for read number formula records + */ +class ReadNumberFormulaRecord extends ReadFormulaRecord + implements NumberFormulaCell +{ + // The logger + private static Logger logger = Logger.getLogger(ReadNumberFormulaRecord.class); + + /** + * Constructor + * + * @param f + */ + public ReadNumberFormulaRecord(FormulaData f) + { + super(f); + } + + /** + * Gets the double contents for this cell. + * + * @return the cell contents + */ + public double getValue() + { + return ( (NumberFormulaCell) getReadFormula()).getValue(); + } + + /** + * Gets the NumberFormat used to format this cell. This is the java + * equivalent of the Excel format + * + * @return the NumberFormat used to format the cell + */ + public NumberFormat getNumberFormat() + { + return ( (NumberFormulaCell) getReadFormula()).getNumberFormat(); + } + + /** + * Error formula specific exception handling. Can't really create + * a formula (as it will look for a cell of that name, so just + * create a STRING record containing the contents + * + * @return the bodged data + */ + protected byte[] handleFormulaException() + { + byte[] expressiondata = null; + byte[] celldata = super.getCellData(); + + // Generate an appropriate dummy formula + WritableWorkbookImpl w = getSheet().getWorkbook(); + FormulaParser parser = new FormulaParser(Double.toString(getValue()), w, w, + w.getSettings()); + + // Get the bytes for the dummy formula + try + { + parser.parse(); + } + catch(FormulaException e2) + { + logger.warn(e2.getMessage()); + } + byte[] formulaBytes = parser.getBytes(); + expressiondata = new byte[formulaBytes.length + 16]; + IntegerHelper.getTwoBytes(formulaBytes.length, expressiondata, 14); + System.arraycopy(formulaBytes, 0, expressiondata, 16, + formulaBytes.length); + + // Set the recalculate on load bit + expressiondata[8] |= 0x02; + + byte[] data = new byte[celldata.length + + expressiondata.length]; + System.arraycopy(celldata, 0, data, 0, celldata.length); + System.arraycopy(expressiondata, 0, data, + celldata.length, expressiondata.length); + + // Store the value in the formula + DoubleHelper.getIEEEBytes(getValue(), data, 6); + + return data; + } + +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/ReadStringFormulaRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/ReadStringFormulaRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/ReadStringFormulaRecord.java 17 Aug 2012 14:51:09 -0000 1.1 @@ -0,0 +1,111 @@ +/********************************************************************* +* +* Copyright (C) 2004 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import common.Assert; +import common.Logger; + +import jxl.StringFormulaCell; +import jxl.biff.FormulaData; +import jxl.biff.IntegerHelper; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; + +/** + * Class for read number formula records + */ +class ReadStringFormulaRecord extends ReadFormulaRecord + implements StringFormulaCell +{ + // the logger + private static Logger logger = Logger.getLogger(ReadFormulaRecord.class); + + /** + * Constructor + * + * @param f + */ + public ReadStringFormulaRecord(FormulaData f) + { + super(f); + } + + /** + * Gets the string contents for this cell. + * + * @return the cell contents + */ + public String getString() + { + return ( (StringFormulaCell) getReadFormula()).getString(); + } + + /** + * String formula specific exception handling. Can't really create + * a formula (as it will look for a cell of that name, so just + * create a STRING record containing the contents + * + * @return the bodged data + */ + protected byte[] handleFormulaException() + { + byte[] expressiondata = null; + byte[] celldata = super.getCellData(); + + // Generate an appropriate dummy formula + WritableWorkbookImpl w = getSheet().getWorkbook(); + FormulaParser parser = new FormulaParser("\"" + getContents() +"\"", w, w, + w.getSettings()); + + // Get the bytes for the dummy formula + try + { + parser.parse(); + } + catch(FormulaException e2) + { + logger.warn(e2.getMessage()); + parser = new FormulaParser("\"ERROR\"", w, w, w.getSettings()); + try {parser.parse();} + catch(FormulaException e3) {Assert.verify(false);} + } + byte[] formulaBytes = parser.getBytes(); + expressiondata = new byte[formulaBytes.length + 16]; + IntegerHelper.getTwoBytes(formulaBytes.length, expressiondata, 14); + System.arraycopy(formulaBytes, 0, expressiondata, 16, + formulaBytes.length); + + // Set the recalculate on load bit + expressiondata[8] |= 0x02; + + byte[] data = new byte[celldata.length + + expressiondata.length]; + System.arraycopy(celldata, 0, data, 0, celldata.length); + System.arraycopy(expressiondata, 0, data, + celldata.length, expressiondata.length); + + // Set the type bits to indicate a string formula + data[6] = 0; + data[12] = -1; + data[13] = -1; + + return data; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/RefModeRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/RefModeRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/RefModeRecord.java 17 Aug 2012 14:51:09 -0000 1.1 @@ -0,0 +1,54 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Stores the reference style option from the options dialog box + */ +class RefModeRecord extends WritableRecordData +{ + /** + * Constructor + */ + public RefModeRecord() + { + super(Type.REFMODE); + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + byte[] data = new byte[2]; + + // Hard code in the style A1 for now + data[0] = 0x1; + + return data; + } +} + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/RefreshAllRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/RefreshAllRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/RefreshAllRecord.java 17 Aug 2012 14:51:12 -0000 1.1 @@ -0,0 +1,70 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which indicates whether or not data ranges and pivot tables + * should be refreshed when the workbook is loaded + */ +class RefreshAllRecord extends WritableRecordData +{ + /** + * The refresh all flag + */ + private boolean refreshall; + /** + * The binary data + */ + private byte[] data; + + /** + * Constructor + * + * @param refresh refresh all flag + */ + public RefreshAllRecord(boolean refresh) + { + super(Type.REFRESHALL); + + refreshall = refresh; + + // Hard code in an unprotected workbook + data = new byte[2]; + + if (refreshall) + { + IntegerHelper.getTwoBytes(1, data, 0); + } + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + return data; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/RightMarginRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/RightMarginRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/RightMarginRecord.java 17 Aug 2012 14:51:10 -0000 1.1 @@ -0,0 +1,33 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; + +/** + * The settings for the left margin + */ +class RightMarginRecord extends MarginRecord +{ + RightMarginRecord(double v) + { + super(Type.RIGHTMARGIN, v); + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/RowRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/RowRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/RowRecord.java 17 Aug 2012 14:51:12 -0000 1.1 @@ -0,0 +1,738 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; + +import common.Logger; + +import jxl.CellType; +import jxl.SheetSettings; +import jxl.WorkbookSettings; +import jxl.biff.CellReferenceHelper; +import jxl.biff.IndexMapping; +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; +import jxl.biff.XFRecord; +import jxl.write.Number; +import jxl.write.WritableSheet; + +/** + * Contains all the cells for a given row in a sheet + */ +class RowRecord extends WritableRecordData +{ + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(RowRecord.class); + + /** + * The binary data + */ + private byte[] data; + /** + * The cells which comprise this row + */ + private CellValue[] cells; + /** + * The height of this row in 1/20ths of a point + */ + private int rowHeight; + /** + * Flag to indicate whether this row is outline collapsed or not + */ + private boolean collapsed; + /** + * The number of this row within the worksheet + */ + private int rowNumber; + /** + * The number of columns in this row. This is the largest column value + 1 + */ + private int numColumns; + /** + * The xfIndex for this row + */ + private int xfIndex; + /** + * The style for this row + */ + private XFRecord style; + /** + * Flag indicating that this row record has an default format + */ + private boolean defaultFormat; + /** + * Flag indicating whether this row matches the default font height + */ + private boolean matchesDefFontHeight; + /** + * The amount to grow the cells array by + */ + private static final int growSize = 10; + + /** + * The maximum integer value that can be squeezed into 30 bits + */ + private static final int maxRKValue = 0x1fffffff; + + /** + * The minimum integer value that can be squeezed into 30 bits + */ + private static final int minRKValue = -0x20000000; + + /** + * Indicates that the row is default height + */ + private static int defaultHeightIndicator = 0xff; + + /** + * The maximum number of columns + */ + private static int maxColumns = 256; + + /** + * The outline level of the row + */ + private int outlineLevel; + + /** + * Is this the icon indicator row of a group? + */ + private boolean groupStart; + + /** + * A handle back to the sheet + */ + private WritableSheet sheet; + + /** + * Constructs an empty row which has the specified row number + * + * @param rn the row number of this row + */ + public RowRecord(int rn, WritableSheet ws) + { + super(Type.ROW); + rowNumber = rn; + cells = new CellValue[0]; + numColumns = 0; + rowHeight = defaultHeightIndicator; + collapsed = false; + matchesDefFontHeight = true; + sheet = ws; + } + + /** + * Sets the height of this row + * + * @param h the row height + */ + public void setRowHeight(int h) + { + if (h == 0) + { + setCollapsed(true); + matchesDefFontHeight = false; + } + else + { + rowHeight = h; + matchesDefFontHeight = false; + } + } + + /** + * Sets the row details based upon the readable row record passed in + * Called when copying spreadsheets + * + * @param height the height of the row record in 1/20ths of a point + * @param mdfh matches the default font height + * @param col the collapsed status of the row + * @param ol the outline level + * @param gs the group start + * @param xf the xfrecord for the row (NULL if no default is set) + */ + void setRowDetails(int height, + boolean mdfh, + boolean col, + int ol, + boolean gs, + XFRecord xfr) + { + rowHeight = height; + collapsed = col; + matchesDefFontHeight = mdfh; + outlineLevel = ol; + groupStart = gs; + + if (xfr != null) + { + defaultFormat = true; + style = xfr; + xfIndex = style.getXFIndex(); + } + } + + /** + * Sets the collapsed status of this row + * + * @param c the collapsed flag + */ + public void setCollapsed(boolean c) + { + collapsed = c; + } + + /** + * Gets the row number of this row + * + * @return the row number + */ + public int getRowNumber() + { + return rowNumber; + } + + /** + * Adds a cell to this row, growing the array of cells as required + * + * @param cv the cell to add + */ + public void addCell(CellValue cv) + { + int col = cv.getColumn(); + + if (col >= maxColumns) + { + logger.warn("Could not add cell at " + + CellReferenceHelper.getCellReference(cv.getRow(), + cv.getColumn()) + + " because it exceeds the maximum column limit"); + return; + } + + // Grow the array if needs be + // Thanks to Brendan for spotting the flaw in merely adding on the + // grow size + if (col >= cells.length) + { + CellValue[] oldCells = cells; + cells = new CellValue[Math.max(oldCells.length + growSize, col+1)]; + System.arraycopy(oldCells, 0, cells, 0, oldCells.length); + oldCells = null; + } + + if (cells[col] != null) + { + cells[col].removeCellFeatures(); + } + + cells[col] = cv; + + numColumns = Math.max(col+1, numColumns); + } + + /** + * Removes a cell from this row + * + * @param col the column at which to remove the cell + */ + public void removeCell(int col) + { + // Grow the array if needs be + if (col >= numColumns) + { + return; + } + + cells[col] = null; + } + + /** + * Writes out the row information data (but not the individual cells) + * + * @exception IOException + * @param outputFile the output file + */ + public void write(File outputFile) throws IOException + { + outputFile.write(this); + } + + /** + * Writes out all the cells in this row. If more than three integer + * values occur consecutively, then a MulRK record is used to group the + * numbers + * + * @exception IOException + * @param outputFile the output file + */ + public void writeCells(File outputFile) + throws IOException + { + // This is the list for integer values + ArrayList integerValues = new ArrayList(); + boolean integerValue = false; + + // Write out all the records + for (int i = 0; i < numColumns; i++) + { + integerValue = false; + if (cells[i] != null) + { + // See if this cell is a 30-bit integer value (without additional + // cell features) + if (cells[i].getType() == CellType.NUMBER) + { + Number nc = (Number) cells[i]; + if (nc.getValue() == (int) nc.getValue() && + nc.getValue() < maxRKValue && + nc.getValue() > minRKValue && + nc.getCellFeatures() == null) + { + integerValue = true; + } + } + + if (integerValue) + { + // This cell is an integer, add it to the list + integerValues.add(cells[i]); + } + else + { + // This cell is not an integer. Write out whatever integers we + // have, and then write out this cell + writeIntegerValues(integerValues, outputFile); + outputFile.write(cells[i]); + + // If the cell is a string formula, write out the string record + // immediately afterwards + if (cells[i].getType() == CellType.STRING_FORMULA) + { + StringRecord sr = new StringRecord(cells[i].getContents()); + outputFile.write(sr); + } + } + } + else + { + // Cell does not exist. Write out the list of integers that + // we have + writeIntegerValues(integerValues, outputFile); + } + } + + // All done. Write out any remaining integer values + writeIntegerValues(integerValues, outputFile); + } + + /** + * Writes out the list of integer values. If there are more than three, + * a MulRK record is used, otherwise a sequence of Numbers is used + * + * @exception IOException + * @param outputFile the output file + * @param integerValues the array of integer values + */ + private void writeIntegerValues(ArrayList integerValues, File outputFile) + throws IOException + { + if (integerValues.size() == 0) + { + return; + } + + if (integerValues.size() >= 3 ) + { + // Write out as a MulRK record + MulRKRecord mulrk = new MulRKRecord(integerValues); + outputFile.write(mulrk); + } + else + { + // Write out as number records + Iterator i = integerValues.iterator(); + while (i.hasNext()) + { + outputFile.write((CellValue) i.next()); + } + } + + // Clear out the list of integerValues + integerValues.clear(); + } + + /** + * Gets the row data to output to file + * + * @return the binary data + */ + public byte[] getData() + { + // Write out the row record + byte[] data = new byte[16]; + + // If the default row height has been changed in the sheet settings, + // then we need to set the rowHeight on this row explicitly, as + // specifying the "match default" flag doesn't work + int rh = rowHeight; + if (sheet.getSettings().getDefaultRowHeight() != + SheetSettings.DEFAULT_DEFAULT_ROW_HEIGHT) + { + // the default row height has been changed. If this row does not + // have a specific row height set on it, then set it to the default + if (rh == defaultHeightIndicator) + { + rh = sheet.getSettings().getDefaultRowHeight(); + } + } + + IntegerHelper.getTwoBytes(rowNumber, data, 0); + IntegerHelper.getTwoBytes(numColumns, data, 4); + IntegerHelper.getTwoBytes(rh, data, 6); + + int options = 0x100 + outlineLevel; + + if (groupStart) + { + options |= 0x10; + } + + if (collapsed) + { + options |= 0x20; + } + + if (!matchesDefFontHeight) + { + options |= 0x40; + } + + if (defaultFormat) + { + options |= 0x80; + options |= (xfIndex << 16); + } + + IntegerHelper.getFourBytes(options, data, 12); + + return data; + } + + /** + * Gets the maximum column value which occurs in this row + * + * @return the maximum column value + */ + public int getMaxColumn() + { + return numColumns; + } + + /** + * Gets the cell which occurs at the specified column value + * + * @param col the colun for which to return the cell + * @return the cell value at the specified position, or null if the column + * is invalid + */ + public CellValue getCell(int col) + { + return (col >= 0 && col < numColumns) ? cells[col] : null; + } + + /** + * Increments the row of this cell by one. Invoked by the sheet when + * inserting rows + */ + void incrementRow() + { + rowNumber++; + + for (int i = 0; i < cells.length; i++) + { + if (cells[i] != null) + { + cells[i].incrementRow(); + } + } + } + + /** + * Decrements the row of this cell by one. Invoked by the sheet when + * removing rows + */ + void decrementRow() + { + rowNumber--; + for (int i = 0; i < cells.length; i++) + { + if (cells[i] != null) + { + cells[i].decrementRow(); + } + } + } + + /** + * Inserts a new column at the position specified + * + * @param col the column to insert + */ + void insertColumn(int col) + { + // Don't bother doing anything unless there are cells after the + // column to be inserted + if (col >= numColumns) + { + return; + } + + if (numColumns >= maxColumns) + { + logger.warn("Could not insert column because maximum column limit has "+ + "been reached"); + return; + } + + // Create a new array to hold the new column. Grow it if need be + CellValue[] oldCells = cells; + + if (numColumns >= cells.length - 1) + { + cells = new CellValue[oldCells.length + growSize]; + } + else + { + cells = new CellValue[oldCells.length]; + } + + // Copy in everything up to the new column + System.arraycopy(oldCells, 0, cells, 0, col); + + // Copy in the remaining cells + System.arraycopy(oldCells, col, cells, col+1, numColumns - col); + + // Increment all the internal column numbers by one + for (int i = col+1; i <= numColumns; i++) + { + if (cells[i] != null) + { + cells[i].incrementColumn(); + } + } + + // Adjust the maximum column record + numColumns++; + } + + /** + * Remove the new column at the position specified + * + * @param col the column to remove + */ + void removeColumn(int col) + { + // Don't bother doing anything unless there are cells after the + // column to be inserted + if (col >= numColumns) + { + return; + } + + // Create a new array to hold the new columns + CellValue[] oldCells = cells; + + cells = new CellValue[oldCells.length]; + + // Copy in everything up to the column + System.arraycopy(oldCells, 0, cells, 0, col); + + // Copy in the remaining cells after the column + System.arraycopy(oldCells, col + 1, cells, col, numColumns - (col+1)); + + // Decrement all the internal column numbers by one + for (int i = col; i < numColumns; i++) + { + if (cells[i] != null) + { + cells[i].decrementColumn(); + } + } + + // Adjust the maximum column record + numColumns--; + } + + /** + * Interrogates whether this row is of default height + * + * @return TRUE if this is set to the default height, FALSE otherwise + */ + public boolean isDefaultHeight() + { + return rowHeight == defaultHeightIndicator; + } + + /** + * Gets the height of the row + * + * @return the row height + */ + public int getRowHeight() + { + return rowHeight; + } + + /** + * Queries whether the row is collapsed + * + * @return the collapsed indicator + */ + public boolean isCollapsed() + { + return collapsed; + } + + /** + * Rationalizes the sheets xf index mapping + * @param xfmapping the index mapping + */ + void rationalize(IndexMapping xfmapping) + { + if (defaultFormat) + { + xfIndex = xfmapping.getNewIndex(xfIndex); + } + } + + /** + * Accessor for the style. The returned value is only non-null if the + * default style is overridden + * + * @return the style + */ + XFRecord getStyle() + { + return style; + } + + /** + * Accessor for the default format flag + * + * @return TRUE if this row has its own default format + */ + boolean hasDefaultFormat() + { + return defaultFormat; + } + + /** + * Accessor for the matches default font height flag + * + * @return TRUE if this row matches the default font height + */ + boolean matchesDefaultFontHeight() + { + return matchesDefFontHeight; + } + + /** + * Accessor for the column's outline level + * + * @return the column's outline level + */ + public int getOutlineLevel() + { + return outlineLevel; + } + + /** + * Accessor for row's groupStart state + * + * @return the row's groupStart state + */ + public boolean getGroupStart() + { + return groupStart; + } + + /** + * Increments the row's outline level. This is how groups are made as well + */ + public void incrementOutlineLevel() + { + outlineLevel++; + } + + /** + * Decrements the row's outline level. This removes it from a grouping + * level. If + * all outline levels are gone the uncollapse the row. + */ + public void decrementOutlineLevel() + { + if (0 < outlineLevel) + { + outlineLevel--; + } + + if (0==outlineLevel) + { + collapsed = false; + } + } + + /** + * Sets the row's outline level + * + * @param level the row's outline level + */ + public void setOutlineLevel(int level) + { + outlineLevel = level; + } + + /** + * Sets the row's group start state + * + * @param value the group start state + */ + public void setGroupStart(boolean value) + { + groupStart = value; + } +} + + + + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/RowsExceededException.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/RowsExceededException.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/RowsExceededException.java 17 Aug 2012 14:51:09 -0000 1.1 @@ -0,0 +1,35 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +/** + * Exception thrown when attempting to add a row to a spreadsheet which + * has already reached the maximum amount + */ +public class RowsExceededException extends JxlWriteException +{ + /** + * Constructor + */ + public RowsExceededException() + { + super(maxRowsExceeded); + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/SCLRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/SCLRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/SCLRecord.java 17 Aug 2012 14:51:09 -0000 1.1 @@ -0,0 +1,64 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan, Adam Caldwell + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which specifies a margin value + */ +class SCLRecord extends WritableRecordData +{ + /** + * The zoom factor + */ + private int zoomFactor; + + /** + * Constructor + * + * @param zf the zoom factor as a percentage + */ + public SCLRecord(int zf) + { + super(Type.SCL); + + zoomFactor = zf; + } + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + byte[] data = new byte[4]; + + int numerator = zoomFactor; + int denominator = 100; + + IntegerHelper.getTwoBytes(numerator,data,0); + IntegerHelper.getTwoBytes(denominator,data,2); + + return data; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/SSTContinueRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/SSTContinueRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/SSTContinueRecord.java 17 Aug 2012 14:51:10 -0000 1.1 @@ -0,0 +1,225 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import java.util.ArrayList; +import java.util.Iterator; + +import jxl.biff.IntegerHelper; +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * A continuation of a shared string table record. + */ +class SSTContinueRecord extends WritableRecordData +{ + /** + * The first string is a special case + */ + private String firstString; + /** + * Indicates whether or not we need to include the length information + * for the first string + */ + private boolean includeLength; + /** + * The length of the first string + */ + private int firstStringLength; + /** + * The list of strings + */ + private ArrayList strings; + /** + * The list of string lengths + */ + private ArrayList stringLengths; + /** + * The binary data + */ + private byte[] data; + /** + * The count of bytes needed so far to contain this record + */ + private int byteCount; + + /** + * The maximum amount of bytes available for the SST record + */ + private static int maxBytes = 8228 - // max length + 4; // standard biff record stuff + + /** + * Constructor + * + * @param numRefs the number of string references in the workbook + * @param s the number of strings + */ + public SSTContinueRecord() + { + super(Type.CONTINUE); + + byteCount = 0; + strings = new ArrayList(50); + stringLengths = new ArrayList(50); + } + + /** + * Adds the first string to this SST record + * + * @param s the string to add + * @param b include the length information for the first string + * @return the number of characters not added + */ + public int setFirstString(String s, boolean b) + { + includeLength = b; + firstStringLength = s.length(); + + int bytes = 0; + + if (!includeLength) + { + bytes = s.length() * 2 + 1; + } + else + { + bytes = s.length() * 2 + 3; + } + + if (bytes <= maxBytes) + { + firstString = s; + byteCount += bytes; + return 0; + } + + // Calculate the number of characters we can add + // The bytes variable will always be an odd number + int charsAvailable = includeLength ? (maxBytes - 4) / 2 : + (maxBytes - 2) / 2; + + // Add what part of the string we can + firstString = s.substring(0, charsAvailable); + byteCount = maxBytes - 1; + + return s.length() - charsAvailable; + } + + /** + * Gets the current offset into this record, excluding the header fields + * + * @return the number of bytes after the header field + */ + public int getOffset() + { + return byteCount; + } + + /** + * Adds a string to this record. It returns the number of string + * characters not added, due to space constraints. In the event + * of this being non-zero, a continue record will be needed + * + * @param s the string to add + * @return the number of characters not added + */ + public int add(String s) + { + int bytes = s.length() * 2 + 3; + + // Must be able to add at least the first character of the string + // onto the SST + if (byteCount >= maxBytes - 5) + { + return s.length(); + } + + stringLengths.add(new Integer(s.length())); + + if (bytes + byteCount < maxBytes) + { + // add the string and return + strings.add(s); + byteCount += bytes; + return 0; + } + + // Calculate the number of characters we can add + int bytesLeft = maxBytes - 3 - byteCount; + int charsAvailable = bytesLeft % 2 == 0 ? bytesLeft / 2 : + (bytesLeft - 1) / 2; + + // Add what part of the string we can + strings.add(s.substring(0, charsAvailable)); + byteCount += charsAvailable * 2 + 3; + + return s.length() - charsAvailable; + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + data = new byte[byteCount]; + + int pos = 0; + + // Write out the first string + if (includeLength) + { + IntegerHelper.getTwoBytes(firstStringLength, data, 0); + data[2] = 0x01; + pos = 3; + } + else + { + // Just include the unicode indicator + data[0] = 0x01; + pos = 1; + } + + StringHelper.getUnicodeBytes(firstString, data, pos); + pos += firstString.length() * 2; + + // Now write out the remainder of the strings + Iterator i = strings.iterator(); + String s = null; + int length = 0; + int count = 0; + while (i.hasNext()) + { + s = (String) i.next(); + length = ( (Integer) stringLengths.get(count)).intValue(); + IntegerHelper.getTwoBytes(length, data, pos); + data[pos+2] = 0x01; + StringHelper.getUnicodeBytes(s, data, pos+3); + pos += s.length() * 2 + 3; + count++; + } + + return data; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/SSTRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/SSTRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/SSTRecord.java 17 Aug 2012 14:51:11 -0000 1.1 @@ -0,0 +1,166 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import java.util.ArrayList; +import java.util.Iterator; + +import jxl.biff.IntegerHelper; +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * A shared string table record. + */ +class SSTRecord extends WritableRecordData +{ + /** + * The number of string references in the workbook + */ + private int numReferences; + /** + * The number of strings in this table + */ + private int numStrings; + /** + * The list of strings + */ + private ArrayList strings; + /** + * The list of string lengths + */ + private ArrayList stringLengths; + /** + * The binary data + */ + private byte[] data; + /** + * The count of bytes needed so far to contain this record + */ + private int byteCount; + + /** + * The maximum amount of bytes available for the SST record + */ + private static int maxBytes = 8228 - // max length + 8 - // bytes for string count fields + 4; // standard biff record header + + /** + * Constructor + * + * @param numRefs the number of string references in the workbook + * @param s the number of strings + */ + public SSTRecord(int numRefs, int s) + { + super(Type.SST); + + numReferences = numRefs; + numStrings = s; + byteCount = 0; + strings = new ArrayList(50); + stringLengths = new ArrayList(50); + } + + /** + * Adds a string to this SST record. It returns the number of string + * characters not added, due to space constraints. In the event + * of this being non-zero, a continue record will be needed + * + * @param s the string to add + * @return the number of characters not added + */ + public int add(String s) + { + int bytes = s.length() * 2 + 3; + + // Must be able to add at least the first character of the string + // onto the SST + if (byteCount >= maxBytes - 5) + { + return s.length() > 0 ? s.length() : -1; // need to return some non-zero + // value in order to force the creation of a continue record + } + + stringLengths.add(new Integer(s.length())); + + if (bytes + byteCount < maxBytes) + { + // add the string and return + strings.add(s); + byteCount += bytes; + return 0; + } + + // Calculate the number of characters we can add + int bytesLeft = maxBytes - 3 - byteCount; + int charsAvailable = bytesLeft % 2 == 0 ? bytesLeft / 2 : + (bytesLeft - 1) / 2; + + // Add what strings we can + strings.add(s.substring(0, charsAvailable)); + byteCount += charsAvailable * 2 + 3; + + return s.length() - charsAvailable; + } + + /** + * Gets the current offset into this record, excluding the header fields + * + * @return the number of bytes after the header field + */ + public int getOffset() + { + return byteCount + 8; + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + data = new byte[byteCount+8]; + IntegerHelper.getFourBytes(numReferences, data, 0); + IntegerHelper.getFourBytes(numStrings, data, 4); + + int pos = 8; + int count = 0; + + Iterator i = strings.iterator(); + String s = null; + int length = 0; + while (i.hasNext()) + { + s = (String) i.next(); + length = ( (Integer) stringLengths.get(count)).intValue(); + IntegerHelper.getTwoBytes(length, data, pos); + data[pos+2] = 0x01; + StringHelper.getUnicodeBytes(s, data, pos+3); + pos += s.length() * 2 + 3; + count++; + } + + return data; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/SaveRecalcRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/SaveRecalcRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/SaveRecalcRecord.java 17 Aug 2012 14:51:09 -0000 1.1 @@ -0,0 +1,68 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Stores the recalculate before save flag as set in the options dialog box + */ +class SaveRecalcRecord extends WritableRecordData +{ + /** + * The binary data for output to file + */ + private byte[] data; + /** + * The recalculate before save flag + */ + private boolean recalc; + + /** + * Constructor + * + * @param r recalculate flag + */ + public SaveRecalcRecord(boolean r) + { + super(Type.SAVERECALC); + recalc = r; + + data = new byte[2]; + + if (recalc) + { + data[0] = 1; + } + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + return data; + } +} + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/ScenarioProtectRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/ScenarioProtectRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/ScenarioProtectRecord.java 17 Aug 2012 14:51:11 -0000 1.1 @@ -0,0 +1,68 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * The protection state for a sheet or workbook + */ +class ScenarioProtectRecord extends WritableRecordData +{ + /** + * The protection state + */ + private boolean protection; + /** + * The binary data + */ + private byte[] data; + + /** + * Constructor + * + * @param prot the protection state + */ + public ScenarioProtectRecord(boolean prot) + { + super(Type.SCENPROTECT); + + protection = prot; + + data = new byte[2]; + + if (protection) + { + IntegerHelper.getTwoBytes(1, data, 0); + } + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + return data; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/SelectionRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/SelectionRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/SelectionRecord.java 17 Aug 2012 14:51:10 -0000 1.1 @@ -0,0 +1,95 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Stores the current selection + */ +class SelectionRecord extends WritableRecordData +{ + /** + * The pane type + */ + private PaneType pane; + + /** + * The top left column in this pane + */ + private int column; + + /** + * The top left row in this pane + */ + private int row; + + // Enumeration for the pane type + private static class PaneType + { + int val; + + PaneType(int v) + {val = v;} + } + + // The pane types + public final static PaneType lowerRight = new PaneType(0); + public final static PaneType upperRight = new PaneType(1); + public final static PaneType lowerLeft = new PaneType(2); + public final static PaneType upperLeft = new PaneType(3); + + /** + * Constructor + */ + public SelectionRecord(PaneType pt, int col, int r) + { + super(Type.SELECTION); + column = col; + row = r; + pane = pt; + } + + /** + * Gets the binary data + * + * @return the binary data + */ + public byte[] getData() + { + // hard code the data in for now + byte[] data = new byte[15]; + + data[0] = (byte) pane.val; + IntegerHelper.getTwoBytes(row, data, 1); + IntegerHelper.getTwoBytes(column, data, 3); + + data[7] = (byte) 0x01; + + IntegerHelper.getTwoBytes(row, data, 9); + IntegerHelper.getTwoBytes(row, data, 11); + data[13] = (byte) column; + data[14] = (byte) column; + + return data; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/SetupRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/SetupRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/SetupRecord.java 17 Aug 2012 14:51:12 -0000 1.1 @@ -0,0 +1,247 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import common.Logger; + +import jxl.SheetSettings; +import jxl.biff.DoubleHelper; +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; +import jxl.format.PageOrder; +import jxl.format.PageOrientation; +import jxl.format.PaperSize; + +/** + * Stores the options and measurements from the Page Setup dialog box + */ +class SetupRecord extends WritableRecordData +{ + /** + * The logger + */ + Logger logger = Logger.getLogger(SetupRecord.class); + + /** + * The binary data for output to file + */ + private byte[] data; + + /** + * The header margin + */ + private double headerMargin; + + /** + * The footer margin + */ + private double footerMargin; + + /** + * The page orientation + */ + private PageOrientation orientation; + + /** + * The page order + */ + private PageOrder order; + + /** + * The paper size + */ + private int paperSize; + + /** + * The scale factor + */ + private int scaleFactor; + + /** + * The page start + */ + private int pageStart; + + /** + * The fit width + */ + private int fitWidth; + + /** + * The fit height + */ + private int fitHeight; + + /** + * The horizontal print resolution + */ + private int horizontalPrintResolution; + + /** + * The vertical print resolution + */ + private int verticalPrintResolution; + + /** + * The number of copies + */ + private int copies; + + /** + * Indicates whether the setup data should be initiliazed in the setup + * box + */ + private boolean initialized; + + /** + * Constructor, taking the sheet settings. This object just + * takes the various fields from the bean in which it is interested + * + * @param the sheet settings + */ + public SetupRecord(SheetSettings s) + { + super(Type.SETUP); + + orientation = s.getOrientation(); + order = s.getPageOrder(); + headerMargin = s.getHeaderMargin(); + footerMargin = s.getFooterMargin(); + paperSize = s.getPaperSize().getValue(); + horizontalPrintResolution = s.getHorizontalPrintResolution(); + verticalPrintResolution = s.getVerticalPrintResolution(); + fitWidth = s.getFitWidth(); + fitHeight = s.getFitHeight(); + pageStart = s.getPageStart(); + scaleFactor = s.getScaleFactor(); + copies = s.getCopies(); + initialized = true; + } + + /** + * Sets the orientation + * + * @param o the orientation + */ + public void setOrientation(PageOrientation o) + { + orientation = o; + } + + /** + * Sets the page order + * + * @param o + */ + public void setOrder(PageOrder o) + { + order = o; + } + + /** + * Sets the header and footer margins + * + * @param hm the header margin + * @param fm the footer margin + */ + public void setMargins(double hm, double fm) + { + headerMargin = hm; + footerMargin = fm; + } + + /** + * Sets the paper size + * + * @param ps the paper size + */ + public void setPaperSize(PaperSize ps) + { + paperSize = ps.getValue(); + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + data = new byte[34]; + + // Paper size + IntegerHelper.getTwoBytes(paperSize, data, 0); + + // Scale factor + IntegerHelper.getTwoBytes(scaleFactor, data, 2); + + // Page start + IntegerHelper.getTwoBytes(pageStart, data, 4); + + // Fit width + IntegerHelper.getTwoBytes(fitWidth, data, 6); + + // Fit height + IntegerHelper.getTwoBytes(fitHeight, data, 8); + + // grbit + int options = 0; + if (order == PageOrder.RIGHT_THEN_DOWN) + { + options |= 0x01; + } + + if (orientation == PageOrientation.PORTRAIT) + { + options |= 0x02; + } + + if (pageStart != 0) + { + options |= 0x80; + } + + if (!initialized) + { + options |= 0x04; + } + + IntegerHelper.getTwoBytes(options, data, 10); + + // print resolution + IntegerHelper.getTwoBytes(horizontalPrintResolution, data, 12); + + // vertical print resolution + IntegerHelper.getTwoBytes(verticalPrintResolution, data, 14); + + // header margin + DoubleHelper.getIEEEBytes(headerMargin, data, 16); + + // footer margin + DoubleHelper.getIEEEBytes(footerMargin, data, 24); + + // Number of copies + IntegerHelper.getTwoBytes(copies, data, 32); + + return data; + } +} + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/SharedStrings.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/SharedStrings.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/SharedStrings.java 17 Aug 2012 14:51:10 -0000 1.1 @@ -0,0 +1,187 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; + +/** + * The list of available shared strings. This class contains + * the labels used for the entire spreadsheet + */ +class SharedStrings +{ + /** + * All the strings in the spreadsheet, keyed on the string itself + */ + private HashMap strings; + + /** + * Contains the same strings, held in a list + */ + private ArrayList stringList; + + /** + * The total occurrence of strings in the workbook + */ + private int totalOccurrences; + + /** + * Constructor + */ + public SharedStrings() + { + strings = new HashMap(100); + stringList = new ArrayList(100); + totalOccurrences = 0; + } + + /** + * Gets the index for the string passed in. If the string is already + * present, then returns the index of that string, otherwise + * creates a new key-index mapping + * + * @param s the string whose index we want + * @return the index of the string + */ + public int getIndex(String s) + { + Integer i = (Integer) strings.get(s); + + if (i == null) + { + i = new Integer(strings.size()); + strings.put(s, i); + stringList.add(s); + } + + totalOccurrences++; + + return i.intValue(); + } + + /** + * Gets the string at the specified index + * + * @param i the index of the string + * @return the string at the specified index + */ + public String get(int i) + { + return (String) stringList.get(i); + } + + /** + * Writes out the shared string table + * + * @param outputFile the binary output file + * @exception IOException + */ + public void write(File outputFile) throws IOException + { + // Thanks to Guenther for contributing the ExtSST implementation portion + // of this method + int charsLeft = 0; + String curString = null; + SSTRecord sst = new SSTRecord(totalOccurrences, stringList.size()); + ExtendedSSTRecord extsst = new ExtendedSSTRecord(stringList.size()); + int bucketSize = extsst.getNumberOfStringsPerBucket(); + + Iterator i = stringList.iterator(); + int stringIndex = 0; + while (i.hasNext() && charsLeft == 0) + { + curString = (String) i.next(); + // offset + header bytes + int relativePosition = sst.getOffset() + 4; + charsLeft = sst.add(curString); + if ((stringIndex % bucketSize) == 0) { + extsst.addString(outputFile.getPos(), relativePosition); + } + stringIndex++; + } + outputFile.write(sst); + + if (charsLeft != 0 || i.hasNext()) + { + // Add the remainder of the string to the continue record + SSTContinueRecord cont = createContinueRecord(curString, + charsLeft, + outputFile); + + // Carry on looping through the array until all the strings are done + while (i.hasNext()) + { + curString = (String) i.next(); + int relativePosition = cont.getOffset() + 4; + charsLeft = cont.add(curString); + if ((stringIndex % bucketSize) == 0) { + extsst.addString(outputFile.getPos(), relativePosition); + } + stringIndex++; + + if (charsLeft != 0) + { + outputFile.write(cont); + cont = createContinueRecord(curString, charsLeft, outputFile); + } + } + + outputFile.write(cont); + } + + outputFile.write(extsst); + } + + /** + * Creates and returns a continue record using the left over bits and + * pieces + */ + private SSTContinueRecord createContinueRecord + (String curString, int charsLeft, File outputFile) throws IOException + { + // Set up the remainder of the string in the continue record + SSTContinueRecord cont = null; + while (charsLeft != 0) + { + cont = new SSTContinueRecord(); + + if (charsLeft == curString.length() || curString.length() == 0) + { + charsLeft = cont.setFirstString(curString, true); + } + else + { + charsLeft = cont.setFirstString + (curString.substring(curString.length() - charsLeft), false); + } + + if (charsLeft != 0) + { + outputFile.write(cont); + cont = new SSTContinueRecord(); + } + } + + return cont; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/SheetCopier.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/SheetCopier.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/SheetCopier.java 17 Aug 2012 14:51:13 -0000 1.1 @@ -0,0 +1,1088 @@ +/********************************************************************* +* +* Copyright (C) 2006 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import java.util.Arrays; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.TreeSet; +import java.util.Iterator; + +import common.Assert; +import common.Logger; + +import jxl.BooleanCell; +import jxl.Cell; +import jxl.CellType; +import jxl.CellView; +import jxl.DateCell; +import jxl.HeaderFooter; +import jxl.Hyperlink; +import jxl.Image; +import jxl.LabelCell; +import jxl.NumberCell; +import jxl.Range; +import jxl.Sheet; +import jxl.SheetSettings; +import jxl.WorkbookSettings; +import jxl.biff.AutoFilter; +import jxl.biff.CellReferenceHelper; +import jxl.biff.ConditionalFormat; +import jxl.biff.DataValidation; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.IndexMapping; +import jxl.biff.NumFormatRecordsException; +import jxl.biff.SheetRangeImpl; +import jxl.biff.XFRecord; +import jxl.biff.drawing.Chart; +import jxl.biff.drawing.ComboBox; +import jxl.biff.drawing.Drawing; +import jxl.biff.drawing.DrawingGroupObject; +import jxl.format.CellFormat; +import jxl.biff.formula.FormulaException; +import jxl.read.biff.SheetImpl; +import jxl.read.biff.NameRecord; +import jxl.read.biff.WorkbookParser; +import jxl.write.Blank; +import jxl.write.Boolean; +import jxl.write.DateTime; +import jxl.write.Formula; +import jxl.write.Label; +import jxl.write.Number; +import jxl.write.WritableCell; +import jxl.write.WritableCellFormat; +import jxl.write.WritableFont; +import jxl.write.WritableHyperlink; +import jxl.write.WritableImage; +import jxl.write.WritableSheet; +import jxl.write.WritableWorkbook; +import jxl.write.WriteException; + +/** + * A transient utility object used to copy sheets. This + * functionality has been farmed out to a different class + * in order to reduce the bloat of the WritableSheetImpl + */ +class SheetCopier +{ + private static Logger logger = Logger.getLogger(SheetCopier.class); + + private SheetImpl fromSheet; + private WritableSheetImpl toSheet; + private WorkbookSettings workbookSettings; + + // Objects used by the sheet + private TreeSet columnFormats; + private FormattingRecords formatRecords; + private ArrayList hyperlinks; + private MergedCells mergedCells; + private ArrayList rowBreaks; + private ArrayList columnBreaks; + private SheetWriter sheetWriter; + private ArrayList drawings; + private ArrayList images; + private ArrayList conditionalFormats; + private AutoFilter autoFilter; + private DataValidation dataValidation; + private ComboBox comboBox; + private PLSRecord plsRecord; + private boolean chartOnly; + private ButtonPropertySetRecord buttonPropertySet; + private int numRows; + private int maxRowOutlineLevel; + private int maxColumnOutlineLevel; + + // Objects used to maintain state during the copy process + private HashMap xfRecords; + private HashMap fonts; + private HashMap formats; + + public SheetCopier(Sheet f, WritableSheet t) + { + fromSheet = (SheetImpl) f; + toSheet = (WritableSheetImpl) t; + workbookSettings = toSheet.getWorkbook().getSettings(); + chartOnly = false; + } + + void setColumnFormats(TreeSet cf) + { + columnFormats = cf; + } + + void setFormatRecords(FormattingRecords fr) + { + formatRecords = fr; + } + + void setHyperlinks(ArrayList h) + { + hyperlinks = h; + } + + void setMergedCells(MergedCells mc) + { + mergedCells = mc; + } + + void setRowBreaks(ArrayList rb) + { + rowBreaks = rb; + } + + void setColumnBreaks(ArrayList cb) + { + columnBreaks = cb; + } + + void setSheetWriter(SheetWriter sw) + { + sheetWriter = sw; + } + + void setDrawings(ArrayList d) + { + drawings = d; + } + + void setImages(ArrayList i) + { + images = i; + } + + void setConditionalFormats(ArrayList cf) + { + conditionalFormats = cf; + } + + AutoFilter getAutoFilter() + { + return autoFilter; + } + + DataValidation getDataValidation() + { + return dataValidation; + } + + ComboBox getComboBox() + { + return comboBox; + } + + PLSRecord getPLSRecord() + { + return plsRecord; + } + + boolean isChartOnly() + { + return chartOnly; + } + + ButtonPropertySetRecord getButtonPropertySet() + { + return buttonPropertySet; + } + + /** + * Copies a sheet from a read-only version to the writable version. + * Performs shallow copies + */ + public void copySheet() + { + shallowCopyCells(); + + // Copy the column info records + jxl.read.biff.ColumnInfoRecord[] readCirs = fromSheet.getColumnInfos(); + + for (int i = 0 ; i < readCirs.length; i++) + { + jxl.read.biff.ColumnInfoRecord rcir = readCirs[i]; + for (int j = rcir.getStartColumn(); j <= rcir.getEndColumn() ; j++) + { + ColumnInfoRecord cir = new ColumnInfoRecord(rcir, j, + formatRecords); + cir.setHidden(rcir.getHidden()); + columnFormats.add(cir); + } + } + + // Copy the hyperlinks + Hyperlink[] hls = fromSheet.getHyperlinks(); + for (int i = 0 ; i < hls.length; i++) + { + WritableHyperlink hr = new WritableHyperlink + (hls[i], toSheet); + hyperlinks.add(hr); + } + + // Copy the merged cells + Range[] merged = fromSheet.getMergedCells(); + + for (int i = 0; i < merged.length; i++) + { + mergedCells.add(new SheetRangeImpl((SheetRangeImpl)merged[i], toSheet)); + } + + // Copy the row properties + try + { + jxl.read.biff.RowRecord[] rowprops = fromSheet.getRowProperties(); + + for (int i = 0; i < rowprops.length; i++) + { + RowRecord rr = toSheet.getRowRecord(rowprops[i].getRowNumber()); + XFRecord format = rowprops[i].hasDefaultFormat() ? + formatRecords.getXFRecord(rowprops[i].getXFIndex()) : null; + rr.setRowDetails(rowprops[i].getRowHeight(), + rowprops[i].matchesDefaultFontHeight(), + rowprops[i].isCollapsed(), + rowprops[i].getOutlineLevel(), + rowprops[i].getGroupStart(), + format); + numRows = Math.max(numRows, rowprops[i].getRowNumber() + 1); + } + } + catch (RowsExceededException e) + { + // Handle the rows exceeded exception - this cannot occur since + // the sheet we are copying from will have a valid number of rows + Assert.verify(false); + } + + // Copy the headers and footers + // sheetWriter.setHeader(new HeaderRecord(si.getHeader())); + // sheetWriter.setFooter(new FooterRecord(si.getFooter())); + + // Copy the page breaks + int[] rowbreaks = fromSheet.getRowPageBreaks(); + + if (rowbreaks != null) + { + for (int i = 0; i < rowbreaks.length; i++) + { + rowBreaks.add(new Integer(rowbreaks[i])); + } + } + + int[] columnbreaks = fromSheet.getColumnPageBreaks(); + + if (columnbreaks != null) + { + for (int i = 0; i < columnbreaks.length; i++) + { + columnBreaks.add(new Integer(columnbreaks[i])); + } + } + + // Copy the charts + sheetWriter.setCharts(fromSheet.getCharts()); + + // Copy the drawings + DrawingGroupObject[] dr = fromSheet.getDrawings(); + for (int i = 0 ; i < dr.length ; i++) + { + if (dr[i] instanceof jxl.biff.drawing.Drawing) + { + WritableImage wi = new WritableImage + (dr[i], toSheet.getWorkbook().getDrawingGroup()); + drawings.add(wi); + images.add(wi); + } + else if (dr[i] instanceof jxl.biff.drawing.Comment) + { + jxl.biff.drawing.Comment c = + new jxl.biff.drawing.Comment(dr[i], + toSheet.getWorkbook().getDrawingGroup(), + workbookSettings); + drawings.add(c); + + // Set up the reference on the cell value + CellValue cv = (CellValue) toSheet.getWritableCell(c.getColumn(), + c.getRow()); + Assert.verify(cv.getCellFeatures() != null); + cv.getWritableCellFeatures().setCommentDrawing(c); + } + else if (dr[i] instanceof jxl.biff.drawing.Button) + { + jxl.biff.drawing.Button b = + new jxl.biff.drawing.Button + (dr[i], + toSheet.getWorkbook().getDrawingGroup(), + workbookSettings); + drawings.add(b); + } + else if (dr[i] instanceof jxl.biff.drawing.ComboBox) + { + jxl.biff.drawing.ComboBox cb = + new jxl.biff.drawing.ComboBox + (dr[i], + toSheet.getWorkbook().getDrawingGroup(), + workbookSettings); + drawings.add(cb); + } + } + + // Copy the data validations + DataValidation rdv = fromSheet.getDataValidation(); + if (rdv != null) + { + dataValidation = new DataValidation(rdv, + toSheet.getWorkbook(), + toSheet.getWorkbook(), + workbookSettings); + int objid = dataValidation.getComboBoxObjectId(); + if (objid != 0) + { + comboBox = (ComboBox) drawings.get(objid); + } + } + + // Copy the conditional formats + ConditionalFormat[] cf = fromSheet.getConditionalFormats(); + if (cf.length > 0) + { + for (int i = 0; i < cf.length ; i++) + { + conditionalFormats.add(cf[i]); + } + } + + // Get the autofilter + autoFilter = fromSheet.getAutoFilter(); + + // Copy the workspace options + sheetWriter.setWorkspaceOptions(fromSheet.getWorkspaceOptions()); + + // Set a flag to indicate if it contains a chart only + if (fromSheet.getSheetBof().isChart()) + { + chartOnly = true; + sheetWriter.setChartOnly(); + } + + // Copy the environment specific print record + if (fromSheet.getPLS() != null) + { + if (fromSheet.getWorkbookBof().isBiff7()) + { + logger.warn("Cannot copy Biff7 print settings record - ignoring"); + } + else + { + plsRecord = new PLSRecord(fromSheet.getPLS()); + } + } + + // Copy the button property set + if (fromSheet.getButtonPropertySet() != null) + { + buttonPropertySet = new ButtonPropertySetRecord + (fromSheet.getButtonPropertySet()); + } + + // Copy the outline levels + maxRowOutlineLevel = fromSheet.getMaxRowOutlineLevel(); + maxColumnOutlineLevel = fromSheet.getMaxColumnOutlineLevel(); + } + + /** + * Copies a sheet from a read-only version to the writable version. + * Performs shallow copies + */ + public void copyWritableSheet() + { + shallowCopyCells(); + + /* + // Copy the column formats + Iterator cfit = fromWritableSheet.columnFormats.iterator(); + while (cfit.hasNext()) + { + ColumnInfoRecord cv = new ColumnInfoRecord + ((ColumnInfoRecord) cfit.next()); + columnFormats.add(cv); + } + + // Copy the merged cells + Range[] merged = fromWritableSheet.getMergedCells(); + + for (int i = 0; i < merged.length; i++) + { + mergedCells.add(new SheetRangeImpl((SheetRangeImpl)merged[i], this)); + } + + // Copy the row properties + try + { + RowRecord[] copyRows = fromWritableSheet.rows; + RowRecord row = null; + for (int i = 0; i < copyRows.length ; i++) + { + row = copyRows[i]; + + if (row != null && + (!row.isDefaultHeight() || + row.isCollapsed())) + { + RowRecord rr = getRowRecord(i); + rr.setRowDetails(row.getRowHeight(), + row.matchesDefaultFontHeight(), + row.isCollapsed(), + row.getStyle()); + } + } + } + catch (RowsExceededException e) + { + // Handle the rows exceeded exception - this cannot occur since + // the sheet we are copying from will have a valid number of rows + Assert.verify(false); + } + + // Copy the horizontal page breaks + rowBreaks = new ArrayList(fromWritableSheet.rowBreaks); + + // Copy the vertical page breaks + columnBreaks = new ArrayList(fromWritableSheet.columnBreaks); + + // Copy the data validations + DataValidation rdv = fromWritableSheet.dataValidation; + if (rdv != null) + { + dataValidation = new DataValidation(rdv, + workbook, + workbook, + workbookSettings); + } + + // Copy the charts + sheetWriter.setCharts(fromWritableSheet.getCharts()); + + // Copy the drawings + DrawingGroupObject[] dr = si.getDrawings(); + for (int i = 0 ; i < dr.length ; i++) + { + if (dr[i] instanceof jxl.biff.drawing.Drawing) + { + WritableImage wi = new WritableImage(dr[i], + workbook.getDrawingGroup()); + drawings.add(wi); + images.add(wi); + } + + // Not necessary to copy the comments, as they will be handled by + // the deep copy of the individual cells + } + + // Copy the workspace options + sheetWriter.setWorkspaceOptions(fromWritableSheet.getWorkspaceOptions()); + + // Copy the environment specific print record + if (fromWritableSheet.plsRecord != null) + { + plsRecord = new PLSRecord(fromWritableSheet.plsRecord); + } + + // Copy the button property set + if (fromWritableSheet.buttonPropertySet != null) + { + buttonPropertySet = new ButtonPropertySetRecord + (fromWritableSheet.buttonPropertySet); + } + */ + } + + /** + * Imports a sheet from a different workbook, doing a deep copy + */ + public void importSheet() + { + xfRecords = new HashMap(); + fonts = new HashMap(); + formats = new HashMap(); + + deepCopyCells(); + + // Copy the column info records + jxl.read.biff.ColumnInfoRecord[] readCirs = fromSheet.getColumnInfos(); + + for (int i = 0 ; i < readCirs.length; i++) + { + jxl.read.biff.ColumnInfoRecord rcir = readCirs[i]; + for (int j = rcir.getStartColumn(); j <= rcir.getEndColumn() ; j++) + { + ColumnInfoRecord cir = new ColumnInfoRecord(rcir, j); + int xfIndex = cir.getXfIndex(); + XFRecord cf = (WritableCellFormat) xfRecords.get(new Integer(xfIndex)); + + if (cf == null) + { + CellFormat readFormat = fromSheet.getColumnView(j).getFormat(); + WritableCellFormat wcf = copyCellFormat(readFormat); + } + + cir.setCellFormat(cf); + cir.setHidden(rcir.getHidden()); + columnFormats.add(cir); + } + } + + // Copy the hyperlinks + Hyperlink[] hls = fromSheet.getHyperlinks(); + for (int i = 0 ; i < hls.length; i++) + { + WritableHyperlink hr = new WritableHyperlink + (hls[i], toSheet); + hyperlinks.add(hr); + } + + // Copy the merged cells + Range[] merged = fromSheet.getMergedCells(); + + for (int i = 0; i < merged.length; i++) + { + mergedCells.add(new SheetRangeImpl((SheetRangeImpl)merged[i], toSheet)); + } + + // Copy the row properties + try + { + jxl.read.biff.RowRecord[] rowprops = fromSheet.getRowProperties(); + + for (int i = 0; i < rowprops.length; i++) + { + RowRecord rr = toSheet.getRowRecord(rowprops[i].getRowNumber()); + XFRecord format = null; + jxl.read.biff.RowRecord rowrec = rowprops[i]; + if (rowrec.hasDefaultFormat()) + { + format = (WritableCellFormat) + xfRecords.get(new Integer(rowrec.getXFIndex())); + + if (format == null) + { + int rownum = rowrec.getRowNumber(); + CellFormat readFormat = fromSheet.getRowView(rownum).getFormat(); + WritableCellFormat wcf = copyCellFormat(readFormat); + } + } + + rr.setRowDetails(rowrec.getRowHeight(), + rowrec.matchesDefaultFontHeight(), + rowrec.isCollapsed(), + rowrec.getOutlineLevel(), + rowrec.getGroupStart(), + format); + numRows = Math.max(numRows, rowprops[i].getRowNumber() + 1); + } + } + catch (RowsExceededException e) + { + // Handle the rows exceeded exception - this cannot occur since + // the sheet we are copying from will have a valid number of rows + Assert.verify(false); + } + + // Copy the headers and footers + // sheetWriter.setHeader(new HeaderRecord(si.getHeader())); + // sheetWriter.setFooter(new FooterRecord(si.getFooter())); + + // Copy the page breaks + int[] rowbreaks = fromSheet.getRowPageBreaks(); + + if (rowbreaks != null) + { + for (int i = 0; i < rowbreaks.length; i++) + { + rowBreaks.add(new Integer(rowbreaks[i])); + } + } + + int[] columnbreaks = fromSheet.getColumnPageBreaks(); + + if (columnbreaks != null) + { + for (int i = 0; i < columnbreaks.length; i++) + { + columnBreaks.add(new Integer(columnbreaks[i])); + } + } + + // Copy the charts + Chart[] fromCharts = fromSheet.getCharts(); + if (fromCharts != null && fromCharts.length > 0) + { + logger.warn("Importing of charts is not supported"); + /* + sheetWriter.setCharts(fromSheet.getCharts()); + IndexMapping xfMapping = new IndexMapping(200); + for (Iterator i = xfRecords.keySet().iterator(); i.hasNext();) + { + Integer key = (Integer) i.next(); + XFRecord xfmapping = (XFRecord) xfRecords.get(key); + xfMapping.setMapping(key.intValue(), xfmapping.getXFIndex()); + } + + IndexMapping fontMapping = new IndexMapping(200); + for (Iterator i = fonts.keySet().iterator(); i.hasNext();) + { + Integer key = (Integer) i.next(); + Integer fontmap = (Integer) fonts.get(key); + fontMapping.setMapping(key.intValue(), fontmap.intValue()); + } + + IndexMapping formatMapping = new IndexMapping(200); + for (Iterator i = formats.keySet().iterator(); i.hasNext();) + { + Integer key = (Integer) i.next(); + Integer formatmap = (Integer) formats.get(key); + formatMapping.setMapping(key.intValue(), formatmap.intValue()); + } + + // Now reuse the rationalization feature on each chart to + // handle the new fonts + for (int i = 0; i < fromCharts.length ; i++) + { + fromCharts[i].rationalize(xfMapping, fontMapping, formatMapping); + } + */ + } + + // Copy the drawings + DrawingGroupObject[] dr = fromSheet.getDrawings(); + + // Make sure the destination workbook has a drawing group + // created in it + if (dr.length > 0 && + toSheet.getWorkbook().getDrawingGroup() == null) + { + toSheet.getWorkbook().createDrawingGroup(); + } + + for (int i = 0 ; i < dr.length ; i++) + { + if (dr[i] instanceof jxl.biff.drawing.Drawing) + { + WritableImage wi = new WritableImage + (dr[i].getX(), dr[i].getY(), + dr[i].getWidth(), dr[i].getHeight(), + dr[i].getImageData()); + toSheet.getWorkbook().addDrawing(wi); + drawings.add(wi); + images.add(wi); + } + else if (dr[i] instanceof jxl.biff.drawing.Comment) + { + jxl.biff.drawing.Comment c = + new jxl.biff.drawing.Comment(dr[i], + toSheet.getWorkbook().getDrawingGroup(), + workbookSettings); + drawings.add(c); + + // Set up the reference on the cell value + CellValue cv = (CellValue) toSheet.getWritableCell(c.getColumn(), + c.getRow()); + Assert.verify(cv.getCellFeatures() != null); + cv.getWritableCellFeatures().setCommentDrawing(c); + } + else if (dr[i] instanceof jxl.biff.drawing.Button) + { + jxl.biff.drawing.Button b = + new jxl.biff.drawing.Button + (dr[i], + toSheet.getWorkbook().getDrawingGroup(), + workbookSettings); + drawings.add(b); + } + else if (dr[i] instanceof jxl.biff.drawing.ComboBox) + { + jxl.biff.drawing.ComboBox cb = + new jxl.biff.drawing.ComboBox + (dr[i], + toSheet.getWorkbook().getDrawingGroup(), + workbookSettings); + drawings.add(cb); + } + } + + // Copy the data validations + DataValidation rdv = fromSheet.getDataValidation(); + if (rdv != null) + { + dataValidation = new DataValidation(rdv, + toSheet.getWorkbook(), + toSheet.getWorkbook(), + workbookSettings); + int objid = dataValidation.getComboBoxObjectId(); + if (objid != 0) + { + comboBox = (ComboBox) drawings.get(objid); + } + } + + // Copy the workspace options + sheetWriter.setWorkspaceOptions(fromSheet.getWorkspaceOptions()); + + // Set a flag to indicate if it contains a chart only + if (fromSheet.getSheetBof().isChart()) + { + chartOnly = true; + sheetWriter.setChartOnly(); + } + + // Copy the environment specific print record + if (fromSheet.getPLS() != null) + { + if (fromSheet.getWorkbookBof().isBiff7()) + { + logger.warn("Cannot copy Biff7 print settings record - ignoring"); + } + else + { + plsRecord = new PLSRecord(fromSheet.getPLS()); + } + } + + // Copy the button property set + if (fromSheet.getButtonPropertySet() != null) + { + buttonPropertySet = new ButtonPropertySetRecord + (fromSheet.getButtonPropertySet()); + } + + importNames(); + + // Copy the outline levels + maxRowOutlineLevel = fromSheet.getMaxRowOutlineLevel(); + maxColumnOutlineLevel = fromSheet.getMaxColumnOutlineLevel(); + } + + /** + * Performs a shallow copy of the specified cell + */ + private WritableCell shallowCopyCell(Cell cell) + { + CellType ct = cell.getType(); + WritableCell newCell = null; + + if (ct == CellType.LABEL) + { + newCell = new Label((LabelCell) cell); + } + else if (ct == CellType.NUMBER) + { + newCell = new Number((NumberCell) cell); + } + else if (ct == CellType.DATE) + { + newCell = new DateTime((DateCell) cell); + } + else if (ct == CellType.BOOLEAN) + { + newCell = new Boolean((BooleanCell) cell); + } + else if (ct == CellType.NUMBER_FORMULA) + { + newCell = new ReadNumberFormulaRecord((FormulaData) cell); + } + else if (ct == CellType.STRING_FORMULA) + { + newCell = new ReadStringFormulaRecord((FormulaData) cell); + } + else if( ct == CellType.BOOLEAN_FORMULA) + { + newCell = new ReadBooleanFormulaRecord((FormulaData) cell); + } + else if (ct == CellType.DATE_FORMULA) + { + newCell = new ReadDateFormulaRecord((FormulaData) cell); + } + else if(ct == CellType.FORMULA_ERROR) + { + newCell = new ReadErrorFormulaRecord((FormulaData) cell); + } + else if (ct == CellType.EMPTY) + { + if (cell.getCellFormat() != null) + { + // It is a blank cell, rather than an empty cell, so + // it may have formatting information, so + // it must be copied + newCell = new Blank(cell); + } + } + + return newCell; + } + + /** + * Performs a deep copy of the specified cell, handling the cell format + * + * @param cell the cell to copy + */ + private WritableCell deepCopyCell(Cell cell) + { + WritableCell c = shallowCopyCell(cell); + + if (c == null) + { + return c; + } + + if (c instanceof ReadFormulaRecord) + { + ReadFormulaRecord rfr = (ReadFormulaRecord) c; + boolean crossSheetReference = !rfr.handleImportedCellReferences + (fromSheet.getWorkbook(), + fromSheet.getWorkbook(), + workbookSettings); + + if (crossSheetReference) + { + try + { + logger.warn("Formula " + rfr.getFormula() + + " in cell " + + CellReferenceHelper.getCellReference(cell.getColumn(), + cell.getRow()) + + " cannot be imported because it references another " + + " sheet from the source workbook"); + } + catch (FormulaException e) + { + logger.warn("Formula in cell " + + CellReferenceHelper.getCellReference(cell.getColumn(), + cell.getRow()) + + " cannot be imported: " + e.getMessage()); + } + + // Create a new error formula and add it instead + c = new Formula(cell.getColumn(), cell.getRow(), "\"ERROR\""); + } + } + + // Copy the cell format + CellFormat cf = c.getCellFormat(); + int index = ( (XFRecord) cf).getXFIndex(); + WritableCellFormat wcf = (WritableCellFormat) + xfRecords.get(new Integer(index)); + + if (wcf == null) + { + wcf = copyCellFormat(cf); + } + + c.setCellFormat(wcf); + + return c; + } + + /** + * Perform a shallow copy of the cells from the specified sheet into this one + */ + void shallowCopyCells() + { + // Copy the cells + int cells = fromSheet.getRows(); + Cell[] row = null; + Cell cell = null; + for (int i = 0; i < cells; i++) + { + row = fromSheet.getRow(i); + + for (int j = 0; j < row.length; j++) + { + cell = row[j]; + WritableCell c = shallowCopyCell(cell); + + // Encase the calls to addCell in a try-catch block + // These should not generate any errors, because we are + // copying from an existing spreadsheet. In the event of + // errors, catch the exception and then bomb out with an + // assertion + try + { + if (c != null) + { + toSheet.addCell(c); + } + } + catch (WriteException e) + { + Assert.verify(false); + } + } + } + numRows = toSheet.getRows(); + } + + /** + * Perform a deep copy of the cells from the specified sheet into this one + */ + void deepCopyCells() + { + // Copy the cells + int cells = fromSheet.getRows(); + Cell[] row = null; + Cell cell = null; + for (int i = 0; i < cells; i++) + { + row = fromSheet.getRow(i); + + for (int j = 0; j < row.length; j++) + { + cell = row[j]; + WritableCell c = deepCopyCell(cell); + + // Encase the calls to addCell in a try-catch block + // These should not generate any errors, because we are + // copying from an existing spreadsheet. In the event of + // errors, catch the exception and then bomb out with an + // assertion + try + { + if (c != null) + { + toSheet.addCell(c); + } + } + catch (WriteException e) + { + Assert.verify(false); + } + } + } + } + + /** + * Returns an initialized copy of the cell format + * + * @param cf the cell format to copy + * @return a deep copy of the cell format + */ + private WritableCellFormat copyCellFormat(CellFormat cf) + { + try + { + // just do a deep copy of the cell format for now. This will create + // a copy of the format and font also - in the future this may + // need to be sorted out + XFRecord xfr = (XFRecord) cf; + WritableCellFormat f = new WritableCellFormat(xfr); + formatRecords.addStyle(f); + + // Maintain the local list of formats + int xfIndex = xfr.getXFIndex(); + xfRecords.put(new Integer(xfIndex), f); + + int fontIndex = xfr.getFontIndex(); + fonts.put(new Integer(fontIndex), new Integer(f.getFontIndex())); + + int formatIndex = xfr.getFormatRecord(); + formats.put(new Integer(formatIndex), new Integer(f.getFormatRecord())); + + return f; + } + catch (NumFormatRecordsException e) + { + logger.warn("Maximum number of format records exceeded. Using " + + "default format."); + + return WritableWorkbook.NORMAL_STYLE; + } + } + + /** + * Imports any names defined on the source sheet to the destination workbook + */ + private void importNames() + { + WorkbookParser fromWorkbook = (WorkbookParser) fromSheet.getWorkbook(); + WritableWorkbook toWorkbook = toSheet.getWorkbook(); + int fromSheetIndex = fromWorkbook.getIndex(fromSheet); + NameRecord[] nameRecords = fromWorkbook.getNameRecords(); + String[] names = toWorkbook.getRangeNames(); + + for (int i = 0 ; i < nameRecords.length ;i++) + { + NameRecord.NameRange[] nameRanges = nameRecords[i].getRanges(); + + for (int j = 0; j < nameRanges.length; j++) + { + int nameSheetIndex = fromWorkbook.getExternalSheetIndex + (nameRanges[j].getExternalSheet()); + + if (fromSheetIndex == nameSheetIndex) + { + String name = nameRecords[i].getName(); + if (Arrays.binarySearch(names, name) < 0) + { + toWorkbook.addNameArea(name, + toSheet, + nameRanges[j].getFirstColumn(), + nameRanges[j].getFirstRow(), + nameRanges[j].getLastColumn(), + nameRanges[j].getLastRow()); + } + else + { + logger.warn("Named range " + name + + " is already present in the destination workbook"); + } + + } + } + } + } + + /** + * Gets the number of rows - allows for the case where formatting has + * been applied to rows, even though the row has no data + * + * @return the number of rows + */ + int getRows() + { + return numRows; + } + + /** + * Accessor for the maximum column outline level + * + * @return the maximum column outline level, or 0 if no outlines/groups + */ + public int getMaxColumnOutlineLevel() + { + return maxColumnOutlineLevel; + } + + /** + * Accessor for the maximum row outline level + * + * @return the maximum row outline level, or 0 if no outlines/groups + */ + public int getMaxRowOutlineLevel() + { + return maxRowOutlineLevel; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/SheetWriter.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/SheetWriter.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/SheetWriter.java 17 Aug 2012 14:51:11 -0000 1.1 @@ -0,0 +1,1147 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.TreeSet; + +import common.Assert; +import common.Logger; + +import jxl.Cell; +import jxl.CellFeatures; +import jxl.Range; +import jxl.SheetSettings; +import jxl.WorkbookSettings; +import jxl.biff.AutoFilter; +import jxl.biff.ConditionalFormat; +import jxl.biff.DataValidation; +import jxl.biff.DataValiditySettingsRecord; +import jxl.biff.DVParser; +import jxl.biff.WorkspaceInformationRecord; +import jxl.biff.XFRecord; +import jxl.biff.drawing.Chart; +import jxl.biff.drawing.SheetDrawingWriter; +import jxl.biff.formula.FormulaException; +import jxl.format.Border; +import jxl.format.BorderLineStyle; +import jxl.format.Colour; +import jxl.write.Blank; +import jxl.write.WritableCell; +import jxl.write.WritableCellFormat; +import jxl.write.WritableHyperlink; +import jxl.write.WriteException; + +/** + * Contains the functionality necessary for writing out a sheet. Originally + * this was incorporated in WritableSheetImpl, but was moved out into + * a dedicated class in order to reduce the over bloated nature of that + * class + */ +final class SheetWriter +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(SheetWriter.class); + + /** + * A handle to the output file which the binary data is written to + */ + private File outputFile; + + /** + * The rows within this sheet + */ + private RowRecord[] rows; + + /** + * A number of rows. This is a count of the maximum row number + 1 + */ + private int numRows; + + /** + * The number of columns. This is a count of the maximum column number + 1 + */ + private int numCols; + + /** + * The page header + */ + private HeaderRecord header; + /** + * The page footer + */ + private FooterRecord footer; + /** + * The settings for the sheet + */ + private SheetSettings settings; + /** + * The settings for the workbook + */ + private WorkbookSettings workbookSettings; + /** + * Array of row page breaks + */ + private ArrayList rowBreaks; + /** + * Array of column page breaks + */ + private ArrayList columnBreaks; + /** + * Array of hyperlinks + */ + private ArrayList hyperlinks; + /** + * Array of conditional formats + */ + private ArrayList conditionalFormats; + /** + * The autofilter info + */ + private AutoFilter autoFilter; + /** + * Array of validated cells + */ + private ArrayList validatedCells; + /** + * The data validation validations + */ + private DataValidation dataValidation; + + /** + * The list of merged ranges + */ + private MergedCells mergedCells; + + /** + * The environment specific print record + */ + private PLSRecord plsRecord; + + /** + * The button property ste + */ + private ButtonPropertySetRecord buttonPropertySet; + + /** + * The workspace options + */ + private WorkspaceInformationRecord workspaceOptions; + /** + * The column format overrides + */ + private TreeSet columnFormats; + + /** + * The list of drawings + */ + private SheetDrawingWriter drawingWriter; + + /** + * Flag indicates that this sheet contains just a chart, and nothing + * else + */ + private boolean chartOnly; + + /** + * The maximum row outline level + */ + private int maxRowOutlineLevel; + + /** + * The maximum column outline level + */ + private int maxColumnOutlineLevel; + + /** + * A handle back to the writable sheet, in order for this class + * to invoke the get accessor methods + */ + private WritableSheetImpl sheet; + + + /** + * Creates a new SheetWriter instance. + * + * @param of the output file + */ + public SheetWriter(File of, + WritableSheetImpl wsi, + WorkbookSettings ws) + { + outputFile = of; + sheet = wsi; + workspaceOptions = new WorkspaceInformationRecord(); + workbookSettings = ws; + chartOnly = false; + drawingWriter = new SheetDrawingWriter(ws); + } + + /** + * Writes out this sheet. First writes out the standard sheet + * information then writes out each row in turn. + * Once all the rows have been written out, it retrospectively adjusts + * the offset references in the file + * + * @exception IOException + */ + public void write() throws IOException + { + Assert.verify(rows != null); + + // This worksheet consists of just one chart, so write it and return + if (chartOnly) + { + drawingWriter.write(outputFile); + return; + } + + BOFRecord bof = new BOFRecord(BOFRecord.sheet); + outputFile.write(bof); + + // Compute the number of blocks of 32 rows that will be needed + int numBlocks = numRows / 32; + if (numRows - numBlocks * 32 != 0) + { + numBlocks++; + } + + int indexPos = outputFile.getPos(); + + // Write the index record out now in order to serve as a place holder + // The bof passed in is the bof of the workbook, not this sheet + IndexRecord indexRecord = new IndexRecord(0, numRows, numBlocks); + outputFile.write(indexRecord); + + if (settings.getAutomaticFormulaCalculation()) + { + CalcModeRecord cmr = new CalcModeRecord(CalcModeRecord.automatic); + outputFile.write(cmr); + } + else + { + CalcModeRecord cmr = new CalcModeRecord(CalcModeRecord.manual); + outputFile.write(cmr); + } + + CalcCountRecord ccr = new CalcCountRecord(0x64); + outputFile.write(ccr); + + RefModeRecord rmr = new RefModeRecord(); + outputFile.write(rmr); + + IterationRecord itr = new IterationRecord(false); + outputFile.write(itr); + + DeltaRecord dtr = new DeltaRecord(0.001); + outputFile.write(dtr); + + SaveRecalcRecord srr = new SaveRecalcRecord + (settings.getRecalculateFormulasBeforeSave()); + outputFile.write(srr); + + PrintHeadersRecord phr = new PrintHeadersRecord + (settings.getPrintHeaders()); + outputFile.write(phr); + + PrintGridLinesRecord pglr = new PrintGridLinesRecord + (settings.getPrintGridLines()); + outputFile.write(pglr); + + GridSetRecord gsr = new GridSetRecord(true); + outputFile.write(gsr); + + GuttersRecord gutr = new GuttersRecord(); + gutr.setMaxColumnOutline(maxColumnOutlineLevel + 1); + gutr.setMaxRowOutline(maxRowOutlineLevel + 1); + + outputFile.write(gutr); + + DefaultRowHeightRecord drhr = new DefaultRowHeightRecord + (settings.getDefaultRowHeight(), + settings.getDefaultRowHeight() != + SheetSettings.DEFAULT_DEFAULT_ROW_HEIGHT); + outputFile.write(drhr); + + if (maxRowOutlineLevel > 0) + { + workspaceOptions.setRowOutlines(true); + } + + if (maxColumnOutlineLevel > 0) + { + workspaceOptions.setColumnOutlines(true); + } + + workspaceOptions.setFitToPages(settings.getFitToPages()); + outputFile.write(workspaceOptions); + + if (rowBreaks.size() > 0) + { + int[] rb = new int[rowBreaks.size()]; + + for (int i = 0; i < rb.length; i++) + { + rb[i] = ( (Integer) rowBreaks.get(i)).intValue(); + } + + HorizontalPageBreaksRecord hpbr = new HorizontalPageBreaksRecord(rb); + outputFile.write(hpbr); + } + + if (columnBreaks.size() > 0) + { + int[] rb = new int[columnBreaks.size()]; + + for (int i = 0; i < rb.length; i++) + { + rb[i] = ( (Integer) columnBreaks.get(i)).intValue(); + } + + VerticalPageBreaksRecord hpbr = new VerticalPageBreaksRecord(rb); + outputFile.write(hpbr); + } + + HeaderRecord header = new HeaderRecord(settings.getHeader().toString()); + outputFile.write(header); + + FooterRecord footer = new FooterRecord(settings.getFooter().toString()); + outputFile.write(footer); + + HorizontalCentreRecord hcr = new HorizontalCentreRecord + (settings.isHorizontalCentre()); + outputFile.write(hcr); + + VerticalCentreRecord vcr = new VerticalCentreRecord + (settings.isVerticalCentre()); + outputFile.write(vcr); + + // Write out the margins if they don't equal the default + if (settings.getLeftMargin() != settings.getDefaultWidthMargin()) + { + MarginRecord mr = new LeftMarginRecord(settings.getLeftMargin()); + outputFile.write(mr); + } + + if (settings.getRightMargin() != settings.getDefaultWidthMargin()) + { + MarginRecord mr = new RightMarginRecord(settings.getRightMargin()); + outputFile.write(mr); + } + + if (settings.getTopMargin() != settings.getDefaultHeightMargin()) + { + MarginRecord mr = new TopMarginRecord(settings.getTopMargin()); + outputFile.write(mr); + } + + if (settings.getBottomMargin() != settings.getDefaultHeightMargin()) + { + MarginRecord mr = new BottomMarginRecord(settings.getBottomMargin()); + outputFile.write(mr); + } + + if (plsRecord != null) + { + outputFile.write(plsRecord); + } + + SetupRecord setup = new SetupRecord(settings); + outputFile.write(setup); + + if (settings.isProtected()) + { + ProtectRecord pr = new ProtectRecord(settings.isProtected()); + outputFile.write(pr); + + ScenarioProtectRecord spr = new ScenarioProtectRecord + (settings.isProtected()); + outputFile.write(spr); + + ObjectProtectRecord opr = new ObjectProtectRecord + (settings.isProtected()); + outputFile.write(opr); + + if (settings.getPassword() != null) + { + PasswordRecord pw = new PasswordRecord(settings.getPassword()); + outputFile.write(pw); + } + else if (settings.getPasswordHash() != 0) + { + PasswordRecord pw = new PasswordRecord(settings.getPasswordHash()); + outputFile.write(pw); + } + } + + indexRecord.setDataStartPosition(outputFile.getPos()); + DefaultColumnWidth dcw = + new DefaultColumnWidth(settings.getDefaultColumnWidth()); + outputFile.write(dcw); + + // Get a handle to the normal styles + WritableCellFormat normalStyle = + sheet.getWorkbook().getStyles().getNormalStyle(); + WritableCellFormat defaultDateFormat = + sheet.getWorkbook().getStyles().getDefaultDateFormat(); + + // Write out all the column formats + ColumnInfoRecord cir = null; + for (Iterator colit = columnFormats.iterator(); colit.hasNext() ; ) + { + cir = (ColumnInfoRecord) colit.next(); + + // Writing out the column info with index 0x100 causes excel to crash + if (cir.getColumn() < 0x100) + { + outputFile.write(cir); + } + + XFRecord xfr = cir.getCellFormat(); + + if (xfr != normalStyle && cir.getColumn() < 0x100) + { + // Make this the format for every cell in the column + Cell[] cells = getColumn(cir.getColumn()); + + for (int i = 0; i < cells.length; i++) + { + if (cells[i] != null && + (cells[i].getCellFormat() == normalStyle || + cells[i].getCellFormat() == defaultDateFormat)) + { + // The cell has no overriding format specified, so + // set it to the column default + ((WritableCell) cells[i]).setCellFormat(xfr); + } + } + } + } + + // Write out the auto filter + if (autoFilter != null) + { + autoFilter.write(outputFile); + } + + DimensionRecord dr = new DimensionRecord(numRows, numCols); + outputFile.write(dr); + + // Write out all the rows, in blocks of 32 + for (int block = 0; block < numBlocks; block++) + { + DBCellRecord dbcell = new DBCellRecord(outputFile.getPos()); + + int blockRows = Math.min(32, numRows - block * 32); + boolean firstRow = true; + + // First write out all the row records + for (int i = block * 32; i < block * 32 + blockRows; i++) + { + if (rows[i] != null) + { + rows[i].write(outputFile); + if (firstRow) + { + dbcell.setCellOffset(outputFile.getPos()); + firstRow = false; + } + } + } + + // Now write out all the cells + for (int i = block * 32; i < block * 32 + blockRows; i++) + { + if (rows[i] != null) + { + dbcell.addCellRowPosition(outputFile.getPos()); + rows[i].writeCells(outputFile); + } + } + + // Now set the current file position in the index record + indexRecord.addBlockPosition(outputFile.getPos()); + + // Set the position of the file pointer and write out the DBCell + // record + dbcell.setPosition(outputFile.getPos()); + outputFile.write(dbcell); + } + + // Do the drawings and charts if enabled + if (!workbookSettings.getDrawingsDisabled()) + { + drawingWriter.write(outputFile); + } + + Window2Record w2r = new Window2Record(settings); + outputFile.write(w2r); + + // Handle the frozen panes + if (settings.getHorizontalFreeze() != 0 || + settings.getVerticalFreeze() != 0) + { + PaneRecord pr = new PaneRecord(settings.getHorizontalFreeze(), + settings.getVerticalFreeze()); + outputFile.write(pr); + + // Handle the selection record. First, there will always be a top left + SelectionRecord sr = new SelectionRecord + (SelectionRecord.upperLeft, 0, 0); + outputFile.write(sr); + + // Top right + if (settings.getHorizontalFreeze() != 0) + { + sr = new SelectionRecord + (SelectionRecord.upperRight, settings.getHorizontalFreeze(), 0); + outputFile.write(sr); + } + + // Bottom left + if (settings.getVerticalFreeze() != 0) + { + sr = new SelectionRecord + (SelectionRecord.lowerLeft, 0, settings.getVerticalFreeze()); + outputFile.write(sr); + } + + // Bottom right + if (settings.getHorizontalFreeze() != 0 && + settings.getVerticalFreeze() != 0) + { + sr = new SelectionRecord + (SelectionRecord.lowerRight, + settings.getHorizontalFreeze(), + settings.getVerticalFreeze()); + outputFile.write(sr); + } + + Weird1Record w1r = new Weird1Record(); + outputFile.write(w1r); + } + else + { + // No frozen panes - just write out the selection record for the + // whole sheet + SelectionRecord sr = new SelectionRecord + (SelectionRecord.upperLeft, 0, 0); + outputFile.write(sr); + } + + // Handle the zoom factor + if (settings.getZoomFactor() != 100) + { + SCLRecord sclr = new SCLRecord(settings.getZoomFactor()); + outputFile.write(sclr); + } + + // Now write out all the merged cells + mergedCells.write(outputFile); + + // Write out all the hyperlinks + Iterator hi = hyperlinks.iterator(); + WritableHyperlink hlr = null; + while (hi.hasNext()) + { + hlr = (WritableHyperlink) hi.next(); + outputFile.write(hlr); + } + + if (buttonPropertySet != null) + { + outputFile.write(buttonPropertySet); + } + + // Write out the data validations + if (dataValidation != null || validatedCells.size() > 0) + { + writeDataValidation(); + } + + // Write out the conditional formats + if (conditionalFormats != null && conditionalFormats.size() > 0) + { + for (Iterator i = conditionalFormats.iterator() ; i.hasNext() ; ) + { + ConditionalFormat cf = (ConditionalFormat) i.next(); + cf.write(outputFile); + } + } + + EOFRecord eof = new EOFRecord(); + outputFile.write(eof); + + // Now the various cross reference offsets have been calculated, + // retrospectively set the values in the output file + outputFile.setData(indexRecord.getData(), indexPos+4); + } + + /** + * Gets the header. Called when copying sheets + * + * @return the page header + */ + final HeaderRecord getHeader() + { + return header; + } + + /** + * Gets the footer. Called when copying sheets + * + * @return the page footer + */ + final FooterRecord getFooter() + { + return footer; + } + + /** + * Sets the data necessary for writing out the sheet. This method must + * be called immediately prior to writing + * + * @param rws the rows in the spreadsheet + */ + void setWriteData(RowRecord[] rws, + ArrayList rb, + ArrayList cb, + ArrayList hl, + MergedCells mc, + TreeSet cf, + int mrol, + int mcol) + { + rows = rws; + rowBreaks = rb; + columnBreaks = cb; + hyperlinks = hl; + mergedCells = mc; + columnFormats = cf; + maxRowOutlineLevel = mrol; + maxColumnOutlineLevel = mcol; + } + + /** + * Sets the dimensions of this spreadsheet. This method must be called + * immediately prior to writing + * + * @param rws the number of rows + * @param cls the number of columns + */ + void setDimensions(int rws, int cls) + { + numRows = rws; + numCols = cls; + } + + /** + * Sets the sheet settings for this particular sheet. Must be + * called immediately prior to writing + * + * @param sr the sheet settings + */ + void setSettings(SheetSettings sr) + { + settings = sr; + } + + /** + * Accessor for the workspace options + * + * @return the workspace options + */ + WorkspaceInformationRecord getWorkspaceOptions() + { + return workspaceOptions; + } + + /** + * Accessor for the workspace options + * + * @param wo the workspace options + */ + void setWorkspaceOptions(WorkspaceInformationRecord wo) + { + if (wo != null) + { + workspaceOptions = wo; + } + } + + + /** + * Sets the charts for this sheet + * + * @param ch the charts + */ + void setCharts(Chart[] ch) + { + drawingWriter.setCharts(ch); + } + + /** + * Sets the drawings on this sheet + * + * @param dr the list of drawings + * @param mod a modified flag + */ + void setDrawings(ArrayList dr, boolean mod) + { + drawingWriter.setDrawings(dr, mod); + } + + /** + * Accessor for the charts on this sheet + * + * @return the charts + */ + Chart[] getCharts() + { + return drawingWriter.getCharts(); + } + + /** + * Check all the merged cells for borders. If the merge record has + * borders, then we need to rejig the cell formats to take account of this. + * This is called by the write method of the WritableWorkbookImpl, so that + * any new XFRecords that are created may be written out with the others + */ + void checkMergedBorders() + { + Range[] mcells = mergedCells.getMergedCells(); + ArrayList borderFormats = new ArrayList(); + for (int mci = 0 ; mci < mcells.length ; mci++) + { + Range range = mcells[mci]; + Cell topLeft = range.getTopLeft(); + XFRecord tlformat = (XFRecord) topLeft.getCellFormat(); + + if (tlformat != null && + tlformat.hasBorders() == true && + !tlformat.isRead()) + { + try + { + CellXFRecord cf1 = new CellXFRecord(tlformat); + Cell bottomRight = range.getBottomRight(); + + cf1.setBorder(Border.ALL, BorderLineStyle.NONE, Colour.BLACK); + cf1.setBorder(Border.LEFT, + tlformat.getBorderLine(Border.LEFT), + tlformat.getBorderColour(Border.LEFT)); + cf1.setBorder(Border.TOP, + tlformat.getBorderLine(Border.TOP), + tlformat.getBorderColour(Border.TOP)); + + if (topLeft.getRow() == bottomRight.getRow()) + { + cf1.setBorder(Border.BOTTOM, + tlformat.getBorderLine(Border.BOTTOM), + tlformat.getBorderColour(Border.BOTTOM)); + } + + if (topLeft.getColumn() == bottomRight.getColumn()) + { + cf1.setBorder(Border.RIGHT, + tlformat.getBorderLine(Border.RIGHT), + tlformat.getBorderColour(Border.RIGHT)); + } + + int index = borderFormats.indexOf(cf1); + if (index != -1) + { + cf1 = (CellXFRecord) borderFormats.get(index); + } + else + { + borderFormats.add(cf1); + } + ( (WritableCell) topLeft).setCellFormat(cf1); + + // Handle the bottom left corner + if (bottomRight.getRow() > topLeft.getRow()) + { + // Handle the corner cell + if (bottomRight.getColumn() != topLeft.getColumn()) + { + CellXFRecord cf2 = new CellXFRecord(tlformat); + cf2.setBorder(Border.ALL, BorderLineStyle.NONE, Colour.BLACK); + cf2.setBorder(Border.LEFT, + tlformat.getBorderLine(Border.LEFT), + tlformat.getBorderColour(Border.LEFT)); + cf2.setBorder(Border.BOTTOM, + tlformat.getBorderLine(Border.BOTTOM), + tlformat.getBorderColour(Border.BOTTOM)); + + index = borderFormats.indexOf(cf2); + if (index != -1) + { + cf2 = (CellXFRecord) borderFormats.get(index); + } + else + { + borderFormats.add(cf2); + } + + sheet.addCell(new Blank(topLeft.getColumn(), + bottomRight.getRow(), cf2)); + } + + // Handle the cells down the left hand side (and along the + // right too, if necessary) + for (int i = topLeft.getRow() + 1; i < bottomRight.getRow() ;i++) + { + CellXFRecord cf3 = new CellXFRecord(tlformat); + cf3.setBorder(Border.ALL, BorderLineStyle.NONE, Colour.BLACK); + cf3.setBorder(Border.LEFT, + tlformat.getBorderLine(Border.LEFT), + tlformat.getBorderColour(Border.LEFT)); + + if (topLeft.getColumn() == bottomRight.getColumn()) + { + cf3.setBorder(Border.RIGHT, + tlformat.getBorderLine(Border.RIGHT), + tlformat.getBorderColour(Border.RIGHT)); + } + + index = borderFormats.indexOf(cf3); + if (index != -1) + { + cf3 = (CellXFRecord) borderFormats.get(index); + } + else + { + borderFormats.add(cf3); + } + + sheet.addCell(new Blank(topLeft.getColumn(), i, cf3)); + } + } + + // Handle the top right corner + if (bottomRight.getColumn() > topLeft.getColumn()) + { + if (bottomRight.getRow() != topLeft.getRow()) + { + // Handle the corner cell + CellXFRecord cf6 = new CellXFRecord(tlformat); + cf6.setBorder(Border.ALL, BorderLineStyle.NONE, Colour.BLACK); + cf6.setBorder(Border.RIGHT, + tlformat.getBorderLine(Border.RIGHT), + tlformat.getBorderColour(Border.RIGHT)); + cf6.setBorder(Border.TOP, + tlformat.getBorderLine(Border.TOP), + tlformat.getBorderColour(Border.TOP)); + index = borderFormats.indexOf(cf6); + if (index != -1) + { + cf6 = (CellXFRecord) borderFormats.get(index); + } + else + { + borderFormats.add(cf6); + } + + sheet.addCell(new Blank(bottomRight.getColumn(), + topLeft.getRow(), cf6)); + } + + // Handle the cells along the right + for (int i = topLeft.getRow() + 1; + i < bottomRight.getRow() ;i++) + { + CellXFRecord cf7 = new CellXFRecord(tlformat); + cf7.setBorder(Border.ALL, BorderLineStyle.NONE, Colour.BLACK); + cf7.setBorder(Border.RIGHT, + tlformat.getBorderLine(Border.RIGHT), + tlformat.getBorderColour(Border.RIGHT)); + + index = borderFormats.indexOf(cf7); + if (index != -1) + { + cf7 = (CellXFRecord) borderFormats.get(index); + } + else + { + borderFormats.add(cf7); + } + + sheet.addCell(new Blank(bottomRight.getColumn(), i, cf7)); + } + + // Handle the cells along the top, and along the bottom too + for (int i = topLeft.getColumn() + 1; + i < bottomRight.getColumn() ;i++) + { + CellXFRecord cf8 = new CellXFRecord(tlformat); + cf8.setBorder(Border.ALL, BorderLineStyle.NONE, Colour.BLACK); + cf8.setBorder(Border.TOP, + tlformat.getBorderLine(Border.TOP), + tlformat.getBorderColour(Border.TOP)); + + if (topLeft.getRow() == bottomRight.getRow()) + { + cf8.setBorder(Border.BOTTOM, + tlformat.getBorderLine(Border.BOTTOM), + tlformat.getBorderColour(Border.BOTTOM)); + } + + index = borderFormats.indexOf(cf8); + if (index != -1) + { + cf8 = (CellXFRecord) borderFormats.get(index); + } + else + { + borderFormats.add(cf8); + } + + sheet.addCell(new Blank(i, topLeft.getRow(), cf8)); + } + } + + // Handle the bottom right corner + if (bottomRight.getColumn() > topLeft.getColumn() || + bottomRight.getRow() > topLeft.getRow()) + { + // Handle the corner cell + CellXFRecord cf4 = new CellXFRecord(tlformat); + cf4.setBorder(Border.ALL, BorderLineStyle.NONE, Colour.BLACK); + cf4.setBorder(Border.RIGHT, + tlformat.getBorderLine(Border.RIGHT), + tlformat.getBorderColour(Border.RIGHT)); + cf4.setBorder(Border.BOTTOM, + tlformat.getBorderLine(Border.BOTTOM), + tlformat.getBorderColour(Border.BOTTOM)); + + if (bottomRight.getRow() == topLeft.getRow()) + { + cf4.setBorder(Border.TOP, + tlformat.getBorderLine(Border.TOP), + tlformat.getBorderColour(Border.TOP)); + } + + if (bottomRight.getColumn() == topLeft.getColumn()) + { + cf4.setBorder(Border.LEFT, + tlformat.getBorderLine(Border.LEFT), + tlformat.getBorderColour(Border.LEFT)); + } + + index = borderFormats.indexOf(cf4); + if (index != -1) + { + cf4 = (CellXFRecord) borderFormats.get(index); + } + else + { + borderFormats.add(cf4); + } + + sheet.addCell(new Blank(bottomRight.getColumn(), + bottomRight.getRow(), cf4)); + + // Handle the cells along the bottom (and along the top + // as well, if appropriate) + for (int i = topLeft.getColumn() + 1; + i < bottomRight.getColumn() ;i++) + { + CellXFRecord cf5 = new CellXFRecord(tlformat); + cf5.setBorder(Border.ALL, BorderLineStyle.NONE, Colour.BLACK); + cf5.setBorder(Border.BOTTOM, + tlformat.getBorderLine(Border.BOTTOM), + tlformat.getBorderColour(Border.BOTTOM)); + + if (topLeft.getRow() == bottomRight.getRow()) + { + cf5.setBorder(Border.TOP, + tlformat.getBorderLine(Border.TOP), + tlformat.getBorderColour(Border.TOP)); + } + + index = borderFormats.indexOf(cf5); + if (index != -1) + { + cf5 = (CellXFRecord) borderFormats.get(index); + } + else + { + borderFormats.add(cf5); + } + + sheet.addCell(new Blank(i, bottomRight.getRow(), cf5)); + } + } + } + catch (WriteException e) + { + // just log e.toString(), not the whole stack trace + logger.warn(e.toString()); + } + } + } + } + + /** + * Get the cells in the column. Don't use the interface method + * getColumn for this as this will create loads of empty cells, + * and we could do without that overhead + */ + private Cell[] getColumn(int col) + { + // Find the last non-null cell + boolean found = false; + int row = numRows - 1; + + while (row >= 0 && !found) + { + if (rows[row] != null && + rows[row].getCell(col) != null) + { + found = true; + } + else + { + row--; + } + } + + // Only create entries for non-empty cells + Cell[] cells = new Cell[row+1]; + + for (int i = 0; i <= row; i++) + { + cells[i] = rows[i] != null ? rows[i].getCell(col) : null; + } + + return cells; + } + + /** + * Sets a flag to indicate that this sheet contains a chart only + */ + void setChartOnly() + { + chartOnly = true; + } + + /** + * Sets the environment specific print record + * + * @param pls the print record + */ + void setPLS(PLSRecord pls) + { + plsRecord = pls; + } + + /** + * Sets the button property set record + * + * @param bps the button property set + */ + void setButtonPropertySet(ButtonPropertySetRecord bps) + { + buttonPropertySet = bps; + } + + /** + * Sets the data validations + * + * @param dv the read-in list of data validations + * @param vc the api manipulated set of data validations + */ + void setDataValidation(DataValidation dv, ArrayList vc) + { + dataValidation = dv; + validatedCells = vc; + } + + /** + * Sets the conditional formats + * + * @param cf the conditonal formats + */ + void setConditionalFormats(ArrayList cf) + { + conditionalFormats = cf; + } + + /** + * Sets the auto filter + * + * @param af the autofilter + */ + void setAutoFilter(AutoFilter af) + { + autoFilter = af; + } + + /** + * Writes out the data validations + */ + private void writeDataValidation() throws IOException + { + if (dataValidation != null && validatedCells.size() == 0) + { + // only data validations are those read in + dataValidation.write(outputFile); + return; + } + + if (dataValidation == null && validatedCells.size() > 0) + { + int comboBoxId = sheet.getComboBox() != null ? + sheet.getComboBox().getObjectId() : DataValidation.DEFAULT_OBJECT_ID; + dataValidation = new DataValidation(comboBoxId, + sheet.getWorkbook(), + sheet.getWorkbook(), + workbookSettings); + + for (Iterator i = validatedCells.iterator(); i.hasNext(); ) + { + CellValue cv = (CellValue) i.next(); + CellFeatures cf = cv.getCellFeatures(); + DataValiditySettingsRecord dvsr = + new DataValiditySettingsRecord(cf.getDVParser()); + dataValidation.add(dvsr); + } + dataValidation.write(outputFile); + return; + } + + // Read and write validations + for (Iterator i = validatedCells.iterator(); i.hasNext(); ) + { + CellValue cv = (CellValue) i.next(); + CellFeatures cf = cv.getCellFeatures(); + DataValiditySettingsRecord dvsr = + new DataValiditySettingsRecord(cf.getDVParser()); + dataValidation.add(dvsr); + } + dataValidation.write(outputFile); + return; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/SortRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/SortRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/SortRecord.java 17 Aug 2012 14:51:08 -0000 1.1 @@ -0,0 +1,118 @@ +/********************************************************************* + * + * Copyright (C) 200r Andrew Khan, Al Mantei + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which specifies sort dialog box values + */ +class SortRecord extends WritableRecordData +{ + private String column1Name; + private String column2Name; + private String column3Name; + private boolean sortColumns; + private boolean sortKey1Desc; + private boolean sortKey2Desc; + private boolean sortKey3Desc; + private boolean sortCaseSensitive; + + /** + * Constructor + * + * @param a Sort Column 1 Name + * @param b Sort Column 2 Name + * @param c Sort Column 3 Name + * @param sc Sort Columns + * @param sk1d Sort Key 1 Descending + * @param sk2d Sort Key 2 Descending + * @param sk3d Sort Key 3 Descending + * @param scs Sort Case Sensitive + */ + public SortRecord(String a, String b, String c, + boolean sc, boolean sk1d, + boolean sk2d, boolean sk3d, boolean scs) + { + super(Type.SORT); + + column1Name = a; + column2Name = b; + column3Name = c; + sortColumns = sc; + sortKey1Desc = sk1d; + sortKey2Desc = sk2d; + sortKey3Desc = sk3d; + sortCaseSensitive = scs; + } + +/** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + int byteCount = 5 + (column1Name.length() * 2) + 1; + if (column2Name.length() > 0) + byteCount += (column2Name.length() * 2) + 1; + if (column3Name.length() > 0) + byteCount += (column3Name.length() * 2) + 1; + byte[] data = new byte[byteCount + 1]; + // there is supposed to be an extra "unused" byte at the end + int optionFlag = 0; + if (sortColumns) + optionFlag = optionFlag | 0x01; + if (sortKey1Desc) + optionFlag = optionFlag | 0x02; + if (sortKey2Desc) + optionFlag = optionFlag | 0x04; + if (sortKey3Desc) + optionFlag = optionFlag | 0x08; + if (sortCaseSensitive) + optionFlag = optionFlag | 0x10; + + data[0] = (byte) optionFlag; + // data[1] is an index for sorting by a list - not implemented + data[2] = (byte) column1Name.length(); + data[3] = (byte) column2Name.length(); + data[4] = (byte) column3Name.length(); + // always write the headings in unicode + data[5] = 0x01; + StringHelper.getUnicodeBytes(column1Name, data, 6); + int curPos = 6 + (column1Name.length() * 2); + if (column2Name.length() > 0) + { + data[curPos++] = 0x01; + StringHelper.getUnicodeBytes(column2Name, data, curPos); + curPos += column2Name.length() * 2; + } + if (column3Name.length() > 0) + { + data[curPos++] = 0x01; + StringHelper.getUnicodeBytes(column3Name, data, curPos); + curPos += column3Name.length() * 2; + } + + return data; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/StringRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/StringRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/StringRecord.java 17 Aug 2012 14:51:11 -0000 1.1 @@ -0,0 +1,62 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Stores the string result of a formula calculation. This record + * occurs immediately after the formula + */ +class StringRecord extends WritableRecordData +{ + /** + * The string value + */ + private String value; + + /** + * Constructor + */ + public StringRecord(String val) + { + super(Type.STRING); + + value = val; + } + + /** + * The binary data to be written out + * + * @return the binary data + */ + public byte[] getData() + { + byte[] data = new byte[value.length() * 2 + 3]; + IntegerHelper.getTwoBytes(value.length(), data, 0); + data[2] = 0x01; // unicode + StringHelper.getUnicodeBytes(value, data, 3); + + return data; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/StyleXFRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/StyleXFRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/StyleXFRecord.java 17 Aug 2012 14:51:08 -0000 1.1 @@ -0,0 +1,67 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.DisplayFormat; +import jxl.biff.FontRecord; +import jxl.biff.XFRecord; + +/** + * A style XF Record + */ +public class StyleXFRecord extends XFRecord +{ + /** + * Constructor + * + * @param fnt the font for this style + * @param form the format of this style + */ + public StyleXFRecord(FontRecord fnt, DisplayFormat form) + { + super(fnt, form); + + setXFDetails(XFRecord.style, 0xfff0); + } + + + /** + * Sets the raw cell options. Called by WritableFormattingRecord + * when setting the built in cell formats + * + * @param opt the cell options + */ + public final void setCellOptions(int opt) + { + super.setXFCellOptions(opt); + } + + /** + * Sets whether or not this XF record locks the cell + * + * @param l the locked flag + * @exception WriteException + */ + public void setLocked(boolean l) + { + super.setXFLocked(l); + } + +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/Styles.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/Styles.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/Styles.java 17 Aug 2012 14:51:09 -0000 1.1 @@ -0,0 +1,225 @@ +/********************************************************************* +* +* Copyright (C) 2004 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import common.Logger; + +import jxl.biff.XFRecord; +import jxl.write.DateFormat; +import jxl.write.DateFormats; +import jxl.write.NumberFormats; +import jxl.write.WritableCellFormat; +import jxl.write.WritableFont; +import jxl.write.WritableWorkbook; + +/** + * A structure containing the styles used by this workbook. This is used + * to enforce thread safety by tying the default styles to a workbook + * instance rather than by initializing them statically + */ +class Styles +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(Styles.class); + + /** + * The default font for Cell formats + */ + private WritableFont arial10pt; + + /** + * The font used for hyperlinks + */ + private WritableFont hyperlinkFont; + + /** + * The default style for cells + */ + private WritableCellFormat normalStyle; + + /** + * The style used for hyperlinks + */ + private WritableCellFormat hyperlinkStyle; + + /** + * A cell format used to hide the cell contents + */ + private WritableCellFormat hiddenStyle; + + /** + * A cell format used for the default date format + */ + private WritableCellFormat defaultDateFormat; + + /** + * Constructor + */ + public Styles() + { + arial10pt = null; + hyperlinkFont = null; + normalStyle = null; + hyperlinkStyle = null; + hiddenStyle = null; + } + + private synchronized void initNormalStyle() + { + normalStyle = new WritableCellFormat(getArial10Pt(), + NumberFormats.DEFAULT); + normalStyle.setFont(getArial10Pt()); + } + + public WritableCellFormat getNormalStyle() + { + if (normalStyle == null) + { + initNormalStyle(); + } + + return normalStyle; + } + + private synchronized void initHiddenStyle() + { + hiddenStyle = new WritableCellFormat + (getArial10Pt(), new DateFormat(";;;")); + } + + public WritableCellFormat getHiddenStyle() + { + if (hiddenStyle == null) + { + initHiddenStyle(); + } + + return hiddenStyle; + } + + private synchronized void initHyperlinkStyle() + { + hyperlinkStyle = new WritableCellFormat(getHyperlinkFont(), + NumberFormats.DEFAULT); + } + + public WritableCellFormat getHyperlinkStyle() + { + if (hyperlinkStyle == null) + { + initHyperlinkStyle(); + } + + return hyperlinkStyle; + } + + private synchronized void initArial10Pt() + { + arial10pt = new WritableFont(WritableWorkbook.ARIAL_10_PT); + } + + public WritableFont getArial10Pt() + { + if (arial10pt == null) + { + initArial10Pt(); + } + + return arial10pt; + } + + private synchronized void initHyperlinkFont() + { + hyperlinkFont = new WritableFont(WritableWorkbook.HYPERLINK_FONT); + } + + public WritableFont getHyperlinkFont() + { + if (hyperlinkFont == null) + { + initHyperlinkFont(); + } + + return hyperlinkFont; + } + + private synchronized void initDefaultDateFormat() + { + defaultDateFormat = new WritableCellFormat(DateFormats.DEFAULT); + } + + public WritableCellFormat getDefaultDateFormat() + { + if (defaultDateFormat == null) + { + initDefaultDateFormat(); + } + + return defaultDateFormat; + } + + /** + * Gets the thread safe version of the cell format passed in. If the + * format is already thread safe (ie. it doesn't use a statically initialized + * format or font) then the same object is simply returned + * This object is already tied to a workbook instance, so no synchronisation + * is necesasry + * + * @param wf a format to verify + * @return the thread safe format + */ + public XFRecord getFormat(XFRecord wf) + { + XFRecord format = wf; + + // Check to see if the format is one of the shared Workbook defaults. If + // so, then get hold of the Workbook's specific instance + if (format == WritableWorkbook.NORMAL_STYLE) + { + format = getNormalStyle(); + } + else if (format == WritableWorkbook.HYPERLINK_STYLE) + { + format = getHyperlinkStyle(); + } + else if (format == WritableWorkbook.HIDDEN_STYLE) + { + format = getHiddenStyle(); + } + else if (format == DateRecord.defaultDateFormat) + { + format = getDefaultDateFormat(); + } + + // Do the same with the statically shared fonts + if (format.getFont() == WritableWorkbook.ARIAL_10_PT) + { + format.setFont(getArial10Pt()); + } + else if (format.getFont() == WritableWorkbook.HYPERLINK_FONT) + { + format.setFont(getHyperlinkFont()); + } + + return format; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/SupbookRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/SupbookRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/SupbookRecord.java 17 Aug 2012 14:51:12 -0000 1.1 @@ -0,0 +1,349 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import common.Assert; +import common.Logger; + +import jxl.WorkbookSettings; +import jxl.biff.EncodedURLHelper; +import jxl.biff.IntegerHelper; +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Stores the supporting workbook information. For files written by + * JExcelApi this will only reference internal sheets + */ +class SupbookRecord extends WritableRecordData +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(SupbookRecord.class); + + /** + * The type of this supbook record + */ + private SupbookType type; + + /** + * The data to be written to the binary file + */ + private byte[] data; + + /** + * The number of sheets - internal & external supbooks only + */ + private int numSheets; + + /** + * The name of the external file + */ + private String fileName; + + /** + * The names of the external sheets + */ + private String[] sheetNames; + + /** + * The workbook settings + */ + private WorkbookSettings workbookSettings; + + /** + * The type of supbook this refers to + */ + private static class SupbookType {}; + + public final static SupbookType INTERNAL = new SupbookType(); + public final static SupbookType EXTERNAL = new SupbookType(); + public final static SupbookType ADDIN = new SupbookType(); + public final static SupbookType LINK = new SupbookType(); + public final static SupbookType UNKNOWN = new SupbookType(); + + /** + * Constructor for add in function names + */ + public SupbookRecord() + { + super(Type.SUPBOOK); + type = ADDIN; + try + { + throw new Exception(); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + /** + * Constructor for internal sheets + */ + public SupbookRecord(int sheets, WorkbookSettings ws) + { + super(Type.SUPBOOK); + + numSheets = sheets; + type = INTERNAL; + workbookSettings = ws; + } + + /** + * Constructor for external sheets + * + * @param fn the filename of the external supbook + * @param ws the workbook settings + */ + public SupbookRecord(String fn, WorkbookSettings ws) + { + super(Type.SUPBOOK); + + fileName = fn; + numSheets = 1; + sheetNames = new String[0]; + workbookSettings = ws; + + type = EXTERNAL; + } + + /** + * Constructor used when copying from an external workbook + */ + public SupbookRecord(jxl.read.biff.SupbookRecord sr, WorkbookSettings ws) + { + super(Type.SUPBOOK); + + workbookSettings = ws; + if (sr.getType() == sr.INTERNAL) + { + type = INTERNAL; + numSheets = sr.getNumberOfSheets(); + } + else if (sr.getType() == sr.EXTERNAL) + { + type = EXTERNAL; + numSheets = sr.getNumberOfSheets(); + fileName = sr.getFileName(); + sheetNames = new String[numSheets]; + + for (int i = 0; i < numSheets; i++) + { + sheetNames[i] = sr.getSheetName(i); + } + } + + if (sr.getType() == sr.ADDIN) + { + logger.warn("Supbook type is addin"); + } + } + + /** + * Initializes an internal supbook record + * + * @param sr the read supbook record to copy from + */ + private void initInternal(jxl.read.biff.SupbookRecord sr) + { + numSheets = sr.getNumberOfSheets(); + initInternal(); + } + + /** + * Initializes an internal supbook record + */ + private void initInternal() + { + data = new byte[4]; + + IntegerHelper.getTwoBytes(numSheets, data, 0); + data[2] = 0x1; + data[3] = 0x4; + type = INTERNAL; + } + + /** + * Adjust the number of internal sheets. Called by WritableSheet when + * a sheet is added or or removed to the workbook + * + * @param sheets the new number of sheets + */ + void adjustInternal(int sheets) + { + Assert.verify(type == INTERNAL); + numSheets = sheets; + initInternal(); + } + + /** + * Initializes an external supbook record + */ + private void initExternal() + { + int totalSheetNameLength = 0; + for (int i = 0; i < numSheets; i++) + { + totalSheetNameLength += sheetNames[i].length(); + } + + byte[] fileNameData = EncodedURLHelper.getEncodedURL(fileName, + workbookSettings); + int dataLength = 2 + // numsheets + 4 + fileNameData.length + + numSheets * 3 + totalSheetNameLength * 2; + + data = new byte[dataLength]; + + IntegerHelper.getTwoBytes(numSheets, data, 0); + + // Add in the file name. Precede with a byte denoting that it is a + // file name + int pos = 2; + IntegerHelper.getTwoBytes(fileNameData.length+1, data, pos); + data[pos+2] = 0; // ascii indicator + data[pos+3] = 1; // file name indicator + System.arraycopy(fileNameData, 0, data, pos+4, fileNameData.length); + + pos += 4 + fileNameData.length; + + // Get the sheet names + for (int i = 0; i < sheetNames.length; i++) + { + IntegerHelper.getTwoBytes(sheetNames[i].length(), data, pos); + data[pos+2] = 1; // unicode indicator + StringHelper.getUnicodeBytes(sheetNames[i], data, pos+3); + pos += 3 + sheetNames[i].length() * 2; + } + } + + /** + * Initializes the supbook record for add in functions + */ + private void initAddin() + { + data = new byte[] {0x1, 0x0, 0x1, 0x3a}; + } + + /** + * The binary data to be written out + * + * @return the binary data + */ + public byte[] getData() + { + if (type == INTERNAL) + { + initInternal(); + } + else if (type == EXTERNAL) + { + initExternal(); + } + else if (type == ADDIN) + { + initAddin(); + } + else + { + logger.warn("unsupported supbook type - defaulting to internal"); + initInternal(); + } + + return data; + } + + /** + * Gets the type of this supbook record + * + * @return the type of this supbook + */ + public SupbookType getType() + { + return type; + } + + /** + * Gets the number of sheets. This will only be non-zero for internal + * and external supbooks + * + * @return the number of sheets + */ + public int getNumberOfSheets() + { + return numSheets; + } + + /** + * Accessor for the file name + * + * @return the file name + */ + public String getFileName() + { + return fileName; + } + + /** + * Adds the worksheet name to this supbook + * + * @param name the worksheet name + * @return the index of this sheet in the supbook record + */ + public int getSheetIndex(String s) + { + boolean found = false; + int sheetIndex = 0; + for (int i = 0; i < sheetNames.length && !found; i++) + { + if (sheetNames[i].equals(s)) + { + found = true; + sheetIndex = 0; + } + } + + if (found) + { + return sheetIndex; + } + + // Grow the array + String[] names = new String[sheetNames.length + 1]; + System.arraycopy(sheetNames, 0, names, 0, sheetNames.length); + names[sheetNames.length] = s; + sheetNames = names; + return sheetNames.length - 1; + } + + /** + * Accessor for the sheet name + * + * @param s the sheet index + */ + public String getSheetName(int s) + { + return sheetNames[s]; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/TabIdRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/TabIdRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/TabIdRecord.java 17 Aug 2012 14:51:11 -0000 1.1 @@ -0,0 +1,62 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Contains an array of sheet tab index numbers + */ +class TabIdRecord extends WritableRecordData +{ + /** + * The binary data + */ + private byte[] data; + + /** + * Constructor + * + * @param sheets the number of sheets + */ + public TabIdRecord(int sheets) + { + super(Type.TABID); + + data = new byte[sheets * 2]; + + for (int i = 0 ; i < sheets; i++) + { + IntegerHelper.getTwoBytes(i+1, data, i * 2); + } + } + + /** + * Gets the data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + return data; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/TopMarginRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/TopMarginRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/TopMarginRecord.java 17 Aug 2012 14:51:11 -0000 1.1 @@ -0,0 +1,33 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; + +/** + * The settings for the left margin + */ +class TopMarginRecord extends MarginRecord +{ + TopMarginRecord(double v) + { + super(Type.TOPMARGIN, v); + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/UsesElfsRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/UsesElfsRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/UsesElfsRecord.java 17 Aug 2012 14:51:10 -0000 1.1 @@ -0,0 +1,66 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Stores the flag which indicates whether the version of excel can + * understand natural language input for formulae + */ +class UsesElfsRecord extends WritableRecordData +{ + /** + * The binary data for output to file + */ + private byte[] data; + /** + * The uses ELFs flag + */ + private boolean usesElfs; + + /** + * Constructor + */ + public UsesElfsRecord() + { + super(Type.USESELFS); + + usesElfs = true; + + data = new byte[2]; + + if (usesElfs) + { + data[0] = 1; + } + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + return data; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/VerticalCentreRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/VerticalCentreRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/VerticalCentreRecord.java 17 Aug 2012 14:51:08 -0000 1.1 @@ -0,0 +1,70 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Indicates whether the centre vertically on page option has been + * set from the options dialog box + */ +class VerticalCentreRecord extends WritableRecordData +{ + /** + * The binary data for output to file + */ + private byte[] data; + /** + * The centre flag + */ + private boolean centre; + + /** + * Constructor + * + * @param ce the centre flag + */ + public VerticalCentreRecord(boolean ce) + { + super(Type.VCENTER); + + centre = ce; + + data = new byte[2]; + + if (centre) + { + data[0] = 1; + } + } + + /** + * Gets the data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + return data; + } +} + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/VerticalPageBreaksRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/VerticalPageBreaksRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/VerticalPageBreaksRecord.java 17 Aug 2012 14:51:09 -0000 1.1 @@ -0,0 +1,72 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Contains the list of explicit horizontal page breaks on the current sheet + */ +class VerticalPageBreaksRecord extends WritableRecordData +{ + /** + * The row breaks + */ + private int[] columnBreaks; + + /** + * Constructor + * + * @param break the row breaks + */ + public VerticalPageBreaksRecord(int[] breaks) + { + super(Type.VERTICALPAGEBREAKS); + + columnBreaks = breaks; + } + + /** + * Gets the binary data to write to the output file + * + * @return the binary data + */ + public byte[] getData() + { + byte[] data = new byte[columnBreaks.length * 6 + 2]; + + // The number of breaks on the list + IntegerHelper.getTwoBytes(columnBreaks.length, data, 0); + int pos = 2; + + for (int i = 0; i < columnBreaks.length; i++) + { + IntegerHelper.getTwoBytes(columnBreaks[i], data, pos); + IntegerHelper.getTwoBytes(0xff, data, pos+4); + pos += 6; + } + + return data; + } +} + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/Weird1Record.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/Weird1Record.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/Weird1Record.java 17 Aug 2012 14:51:11 -0000 1.1 @@ -0,0 +1,51 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Don't know what this does - something to do with freezing panes I think + */ +class Weird1Record extends WritableRecordData +{ + /** + * Constructor + */ + public Weird1Record() + { + super(Type.WEIRD1); + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + byte[] data = new byte[6]; + + data[2] = 0x37; + + return data; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/Window1Record.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/Window1Record.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/Window1Record.java 17 Aug 2012 14:51:11 -0000 1.1 @@ -0,0 +1,83 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Contains workbook level windowing attributes + */ +class Window1Record extends WritableRecordData +{ + /** + * The binary data + */ + private byte[] data; + + /** + * The selected sheet + */ + private int selectedSheet; + + /** + * Constructor + */ + public Window1Record(int selSheet) + { + super(Type.WINDOW1); + + selectedSheet = selSheet; + + // hard code the data in for now + data = new byte[] + {(byte) 0x68, + (byte) 0x1, + (byte) 0xe, + (byte) 0x1, + (byte) 0x5c, + (byte) 0x3a, + (byte) 0xbe, + (byte) 0x23, + (byte) 0x38, + (byte) 0, + (byte) 0, + (byte) 0, + (byte) 0, + (byte) 0, + (byte) 0x1, + (byte) 0, + (byte) 0x58, + (byte) 0x2 }; + + IntegerHelper.getTwoBytes(selectedSheet, data, 10); + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + return data; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/Window2Record.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/Window2Record.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/Window2Record.java 17 Aug 2012 14:51:11 -0000 1.1 @@ -0,0 +1,106 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.SheetSettings; +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Contains the window attributes for a worksheet + */ +class Window2Record extends WritableRecordData +{ + /** + * The binary data for output to file + */ + private byte[] data; + + /** + * Constructor + */ + public Window2Record(SheetSettings settings) + { + super(Type.WINDOW2); + + int options = 0; + + options |= 0x0; // display formula values, not formulas + + if (settings.getShowGridLines()) + { + options |= 0x02; + } + + options |= 0x04; // display row and column headings + + options |= 0x0; // panes should be not frozen + + if (settings.getDisplayZeroValues()) + { + options |= 0x10; + } + + options |= 0x20; // default header + + options |= 0x80; // display outline symbols + + // Handle the freeze panes + if (settings.getHorizontalFreeze() != 0 || + settings.getVerticalFreeze() != 0) + { + options |= 0x08; + options |= 0x100; + } + + // Handle the selected flag + if (settings.isSelected()) + { + options |= 0x600; + } + + // Handle the view mode + if (settings.getPageBreakPreviewMode()) + { + options |= 0x800; + } + + // hard code the data in for now + data = new byte[18]; + IntegerHelper.getTwoBytes(options, data, 0); + IntegerHelper.getTwoBytes(0x40, data, 6); // grid line colour + IntegerHelper.getTwoBytes(settings.getPageBreakPreviewMagnification(), + data, 10); + IntegerHelper.getTwoBytes(settings.getNormalMagnification(), + data, 12); + + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + return data; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/WindowProtectRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/WindowProtectRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/WindowProtectRecord.java 17 Aug 2012 14:51:12 -0000 1.1 @@ -0,0 +1,68 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Stores an option from the Protect Workbook dialog box + */ +class WindowProtectRecord extends WritableRecordData +{ + /** + * Protect flag + */ + private boolean protection; + /** + * The binary data + */ + private byte[] data; + + /** + * Constructor + * + * @param prot the protect flag + */ + public WindowProtectRecord(boolean prot) + { + super(Type.WINDOWPROTECT); + + protection = prot; + + data = new byte[2]; + + if (protection) + { + IntegerHelper.getTwoBytes(1, data, 0); + } + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + return data; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/WritableFontRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/WritableFontRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/WritableFontRecord.java 17 Aug 2012 14:51:09 -0000 1.1 @@ -0,0 +1,174 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.FontRecord; +import jxl.format.Font; +import jxl.write.WriteException; + +/** + * A writable Font record. This class intercepts any set accessor calls + * and throws and exception if the Font is already initialized + */ +public class WritableFontRecord extends FontRecord +{ + /** + * Constructor, used when creating a new font for writing out. + * + * @param bold the bold indicator + * @param ps the point size + * @param us the underline style + * @param fn the name + * @param it italicised indicator + * @param c the colour + * @param ss the script style + */ + protected WritableFontRecord(String fn, int ps, int bold, boolean it, + int us, int ci, int ss) + { + super(fn, ps, bold, it, us, ci, ss); + } + + /** + * Publicly available copy constructor + * + * @param the font to copy + */ + protected WritableFontRecord(Font f) + { + super(f); + } + + + /** + * Sets the point size for this font, if the font hasn't been initialized + * + * @param pointSize the point size + * @exception WriteException, if this font is already in use elsewhere + */ + protected void setPointSize(int pointSize) throws WriteException + { + if (isInitialized()) + { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + + super.setFontPointSize(pointSize); + } + + /** + * Sets the bold style for this font, if the font hasn't been initialized + * + * @param boldStyle the bold style + * @exception WriteException, if this font is already in use elsewhere + */ + protected void setBoldStyle(int boldStyle) throws WriteException + { + if (isInitialized()) + { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + + super.setFontBoldStyle(boldStyle); + } + + /** + * Sets the italic indicator for this font, if the font hasn't been + * initialized + * + * @param italic the italic flag + * @exception WriteException, if this font is already in use elsewhere + */ + protected void setItalic(boolean italic) throws WriteException + { + if (isInitialized()) + { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + + super.setFontItalic(italic); + } + + /** + * Sets the underline style for this font, if the font hasn't been + * initialized + * + * @param us the underline style + * @exception WriteException, if this font is already in use elsewhere + */ + protected void setUnderlineStyle(int us) throws WriteException + { + if (isInitialized()) + { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + + super.setFontUnderlineStyle(us); + } + + /** + * Sets the colour for this font, if the font hasn't been + * initialized + * + * @param colour the colour + * @exception WriteException, if this font is already in use elsewhere + */ + protected void setColour(int colour) throws WriteException + { + if (isInitialized()) + { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + + super.setFontColour(colour); + } + + /** + * Sets the script style (eg. superscript, subscript) for this font, + * if the font hasn't been initialized + * + * @param scriptStyle the colour + * @exception WriteException, if this font is already in use elsewhere + */ + protected void setScriptStyle(int scriptStyle) throws WriteException + { + if (isInitialized()) + { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + + super.setFontScriptStyle(scriptStyle); + } + + /** + * Sets the struck out flag + * + * @param so TRUE if the font is struck out, false otherwise + * @exception WriteException, if this font is already in use elsewhere + */ + protected void setStruckout(boolean os) throws WriteException + { + if (isInitialized()) + { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + super.setFontStruckout(os); + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/WritableFonts.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/WritableFonts.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/WritableFonts.java 17 Aug 2012 14:51:12 -0000 1.1 @@ -0,0 +1,51 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Fonts; +import jxl.write.WritableFont; + +/** + * A container for the list of fonts used in this workbook The writable + * subclass instantiates the predetermined list of fonts available to + * users of the writable API + */ +public class WritableFonts extends Fonts +{ + /** + * Constructor. Creates the predetermined list of fonts + */ + public WritableFonts(WritableWorkbookImpl w) + { + super(); + + addFont(w.getStyles().getArial10Pt()); + + // Create the default fonts + WritableFont f = new WritableFont(WritableFont.ARIAL); + addFont(f); + + f = new WritableFont(WritableFont.ARIAL); + addFont(f); + + f = new WritableFont(WritableFont.ARIAL); + addFont(f); + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/WritableFormattingRecords.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/WritableFormattingRecords.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/WritableFormattingRecords.java 17 Aug 2012 14:51:08 -0000 1.1 @@ -0,0 +1,231 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import common.Assert; + +import jxl.biff.Fonts; +import jxl.biff.FormattingRecords; +import jxl.biff.NumFormatRecordsException; +import jxl.write.NumberFormats; +import jxl.write.WritableCellFormat; + + +/** + * Handles the Format and XF record indexing. The writable subclass + * instantiates the predetermined list of XF records and formats + * present in every Excel Workbook + */ +public class WritableFormattingRecords extends FormattingRecords +{ + /** + * The statically defined normal style + */ + public static WritableCellFormat normalStyle; + + /** + * Constructor. Instantiates the prerequisite list of formats and + * styles required by all Excel workbooks + * + * @param f the list of Fonts + * @param styles the list of style clones + */ + public WritableFormattingRecords(Fonts f, Styles styles) + { + super(f); + + try + { + // Hard code all the styles + StyleXFRecord sxf = new StyleXFRecord + (styles.getArial10Pt(),NumberFormats.DEFAULT); + sxf.setLocked(true); + addStyle(sxf); + + sxf = new StyleXFRecord(getFonts().getFont(1),NumberFormats.DEFAULT); + sxf.setLocked(true); + sxf.setCellOptions(0xf400); + addStyle(sxf); + + sxf = new StyleXFRecord(getFonts().getFont(1),NumberFormats.DEFAULT); + sxf.setLocked(true); + sxf.setCellOptions(0xf400); + addStyle(sxf); + + sxf = new StyleXFRecord(getFonts().getFont(1),NumberFormats.DEFAULT); + sxf.setLocked(true); + sxf.setCellOptions(0xf400); + addStyle(sxf); + + sxf = new StyleXFRecord(getFonts().getFont(2),NumberFormats.DEFAULT); + sxf.setLocked(true); + sxf.setCellOptions(0xf400); + addStyle(sxf); + + sxf = new StyleXFRecord(getFonts().getFont(3),NumberFormats.DEFAULT); + sxf.setLocked(true); + sxf.setCellOptions(0xf400); + addStyle(sxf); + + sxf = new StyleXFRecord(styles.getArial10Pt(), + NumberFormats.DEFAULT); + sxf.setLocked(true); + sxf.setCellOptions(0xf400); + addStyle(sxf); + + sxf = new StyleXFRecord(styles.getArial10Pt(), + NumberFormats.DEFAULT); + sxf.setLocked(true); + sxf.setCellOptions(0xf400); + addStyle(sxf); + + sxf = new StyleXFRecord(styles.getArial10Pt(), + NumberFormats.DEFAULT); + sxf.setLocked(true); + sxf.setCellOptions(0xf400); + addStyle(sxf); + + sxf = new StyleXFRecord(styles.getArial10Pt(), + NumberFormats.DEFAULT); + sxf.setLocked(true); + sxf.setCellOptions(0xf400); + addStyle(sxf); + + sxf = new StyleXFRecord(styles.getArial10Pt(), + NumberFormats.DEFAULT); + sxf.setLocked(true); + sxf.setCellOptions(0xf400); + addStyle(sxf); + + sxf = new StyleXFRecord(styles.getArial10Pt(), + NumberFormats.DEFAULT); + sxf.setLocked(true); + sxf.setCellOptions(0xf400); + addStyle(sxf); + + sxf = new StyleXFRecord(styles.getArial10Pt(), + NumberFormats.DEFAULT); + sxf.setLocked(true); + sxf.setCellOptions(0xf400); + addStyle(sxf); + + sxf = new StyleXFRecord(styles.getArial10Pt(), + NumberFormats.DEFAULT); + sxf.setLocked(true); + sxf.setCellOptions(0xf400); + addStyle(sxf); + + sxf = new StyleXFRecord(styles.getArial10Pt(), + NumberFormats.DEFAULT); + sxf.setLocked(true); + sxf.setCellOptions(0xf400); + addStyle(sxf); + + // That's the end of the built ins. Write the normal style + // cell XF here + addStyle(styles.getNormalStyle()); + + // Continue with "user defined" styles + sxf = new StyleXFRecord(getFonts().getFont(1), + NumberFormats.FORMAT7); + sxf.setLocked(true); + sxf.setCellOptions(0xf800); + addStyle(sxf); + + sxf = new StyleXFRecord(getFonts().getFont(1), + NumberFormats.FORMAT5); + sxf.setLocked(true); + sxf.setCellOptions(0xf800); + addStyle(sxf); + + sxf = new StyleXFRecord(getFonts().getFont(1), + NumberFormats.FORMAT8); + sxf.setLocked(true); + sxf.setCellOptions(0xf800); + addStyle(sxf); + + sxf = new StyleXFRecord(getFonts().getFont(1), + NumberFormats.FORMAT6); + sxf.setLocked(true); + sxf.setCellOptions(0xf800); + addStyle(sxf); + + sxf = new StyleXFRecord(getFonts().getFont(1), + NumberFormats.PERCENT_INTEGER); + sxf.setLocked(true); + sxf.setCellOptions(0xf800); + addStyle(sxf); + + // Hard code in the pre-defined number formats for now + /* + FormatRecord fr = new FormatRecord + ("\"$\"#,##0_);\\(\"$\"#,##0\\)",5); + addFormat(fr); + + fr = new FormatRecord + ("\"$\"#,##0_);[Red]\\(\"$\"#,##0\\)", 6); + addFormat(fr); + + fr = new FormatRecord + ("\"$\"#,##0.00_);\\(\"$\"#,##0.00\\)", 7); + addFormat(fr); + + fr = new FormatRecord + ("\"$\"#,##0.00_);[Red]\\(\"$\"#,##0.00\\)", 8); + addFormat(fr); + + fr = new FormatRecord + ("_(\"$\"* #,##0_);_(\"$\"* \\(#,##0\\);_(\"$\"* \"-\"_);_(@_)", + 0x2a); + // outputFile.write(fr); + + fr = new FormatRecord + ("_(* #,##0_);_(* \\(#,##0\\);_(* \"-\"_);_(@_)", + 0x2e); + // outputFile.write(fr); + + fr = new FormatRecord + ("_(\"$\"* #,##0.00_);_(\"$\"* \\(#,##0.00\\);_(\"$\"* \"-\"??_);_(@_)", + 0x2c); + // outputFile.write(fr); + + fr = new FormatRecord + ("_(* #,##0.00_);_(* \\(#,##0.00\\);_(* \"-\"??_);_(@_)", + 0x2b); + // outputFile.write(fr); + */ + } + catch (NumFormatRecordsException e) + { + // This should not happen yet, since we are just creating the file. + // Bomb out + Assert.verify(false, e.getMessage()); + } + } +} + + + + + + + + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/WritableSheetCopier.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/WritableSheetCopier.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/WritableSheetCopier.java 17 Aug 2012 14:51:10 -0000 1.1 @@ -0,0 +1,597 @@ +/********************************************************************* +* +* Copyright (C) 2006 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import java.util.Arrays; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.TreeSet; +import java.util.Iterator; + +import common.Assert; +import common.Logger; + +import jxl.BooleanCell; +import jxl.Cell; +import jxl.CellType; +import jxl.CellView; +import jxl.DateCell; +import jxl.HeaderFooter; +import jxl.Hyperlink; +import jxl.Image; +import jxl.LabelCell; +import jxl.NumberCell; +import jxl.Range; +import jxl.Sheet; +import jxl.SheetSettings; +import jxl.WorkbookSettings; +import jxl.biff.AutoFilter; +import jxl.biff.CellReferenceHelper; +import jxl.biff.ConditionalFormat; +import jxl.biff.DataValidation; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.IndexMapping; +import jxl.biff.NumFormatRecordsException; +import jxl.biff.SheetRangeImpl; +import jxl.biff.WorkspaceInformationRecord; +import jxl.biff.XFRecord; +import jxl.biff.drawing.Chart; +import jxl.biff.drawing.ComboBox; +import jxl.biff.drawing.Drawing; +import jxl.biff.drawing.DrawingGroupObject; +import jxl.format.CellFormat; +import jxl.biff.formula.FormulaException; +import jxl.read.biff.SheetImpl; +import jxl.read.biff.NameRecord; +import jxl.read.biff.WorkbookParser; +import jxl.write.Blank; +import jxl.write.Boolean; +import jxl.write.DateTime; +import jxl.write.Formula; +import jxl.write.Label; +import jxl.write.Number; +import jxl.write.WritableCell; +import jxl.write.WritableCellFormat; +import jxl.write.WritableFont; +import jxl.write.WritableHyperlink; +import jxl.write.WritableImage; +import jxl.write.WritableSheet; +import jxl.write.WritableWorkbook; +import jxl.write.WriteException; + +/** + * A transient utility object used to copy sheets. This + * functionality has been farmed out to a different class + * in order to reduce the bloat of the WritableSheetImpl + */ +class WritableSheetCopier +{ + private static Logger logger = Logger.getLogger(SheetCopier.class); + + private WritableSheetImpl fromSheet; + private WritableSheetImpl toSheet; + private WorkbookSettings workbookSettings; + + // Objects used by the sheet + private TreeSet fromColumnFormats; + private TreeSet toColumnFormats; + private MergedCells fromMergedCells; + private MergedCells toMergedCells; + private RowRecord[] fromRows; + private ArrayList fromRowBreaks; + private ArrayList fromColumnBreaks; + private ArrayList toRowBreaks; + private ArrayList toColumnBreaks; + private DataValidation fromDataValidation; + private DataValidation toDataValidation; + private SheetWriter sheetWriter; + private ArrayList fromDrawings; + private ArrayList toDrawings; + private ArrayList toImages; + private WorkspaceInformationRecord fromWorkspaceOptions; + private PLSRecord fromPLSRecord; + private PLSRecord toPLSRecord; + private ButtonPropertySetRecord fromButtonPropertySet; + private ButtonPropertySetRecord toButtonPropertySet; + private ArrayList fromHyperlinks; + private ArrayList toHyperlinks; + private int numRows; + private int maxRowOutlineLevel; + private int maxColumnOutlineLevel; + + + private boolean chartOnly; + private FormattingRecords formatRecords; + + + + // Objects used to maintain state during the copy process + private HashMap xfRecords; + private HashMap fonts; + private HashMap formats; + + public WritableSheetCopier(WritableSheet f, WritableSheet t) + { + fromSheet = (WritableSheetImpl) f; + toSheet = (WritableSheetImpl) t; + workbookSettings = toSheet.getWorkbook().getSettings(); + chartOnly = false; + } + + void setColumnFormats(TreeSet fcf, TreeSet tcf) + { + fromColumnFormats = fcf; + toColumnFormats = tcf; + } + + void setMergedCells(MergedCells fmc, MergedCells tmc) + { + fromMergedCells = fmc; + toMergedCells = tmc; + } + + void setRows(RowRecord[] r) + { + fromRows = r; + } + + void setRowBreaks(ArrayList frb, ArrayList trb) + { + fromRowBreaks = frb; + toRowBreaks = trb; + } + + void setColumnBreaks(ArrayList fcb, ArrayList tcb) + { + fromColumnBreaks = fcb; + toColumnBreaks = tcb; + } + + void setDrawings(ArrayList fd, ArrayList td, ArrayList ti) + { + fromDrawings = fd; + toDrawings = td; + toImages = ti; + } + + void setHyperlinks(ArrayList fh, ArrayList th) + { + fromHyperlinks = fh; + toHyperlinks = th; + } + + void setWorkspaceOptions(WorkspaceInformationRecord wir) + { + fromWorkspaceOptions = wir; + } + + void setDataValidation(DataValidation dv) + { + fromDataValidation = dv; + } + + void setPLSRecord(PLSRecord plsr) + { + fromPLSRecord = plsr; + } + + void setButtonPropertySetRecord(ButtonPropertySetRecord bpsr) + { + fromButtonPropertySet = bpsr; + } + + void setSheetWriter(SheetWriter sw) + { + sheetWriter = sw; + } + + + DataValidation getDataValidation() + { + return toDataValidation; + } + + PLSRecord getPLSRecord() + { + return toPLSRecord; + } + + boolean isChartOnly() + { + return chartOnly; + } + + ButtonPropertySetRecord getButtonPropertySet() + { + return toButtonPropertySet; + } + + /** + * Copies a sheet from a read-only version to the writable version. + * Performs shallow copies + */ + public void copySheet() + { + shallowCopyCells(); + + // Copy the column formats + Iterator cfit = fromColumnFormats.iterator(); + while (cfit.hasNext()) + { + ColumnInfoRecord cv = new ColumnInfoRecord + ((ColumnInfoRecord) cfit.next()); + toColumnFormats.add(cv); + } + + // Copy the merged cells + Range[] merged = fromMergedCells.getMergedCells(); + + for (int i = 0; i < merged.length; i++) + { + toMergedCells.add(new SheetRangeImpl((SheetRangeImpl)merged[i], + toSheet)); + } + + try + { + RowRecord row = null; + RowRecord newRow = null; + for (int i = 0; i < fromRows.length ; i++) + { + row = fromRows[i]; + + if (row != null && + (!row.isDefaultHeight() || + row.isCollapsed())) + { + newRow = toSheet.getRowRecord(i); + newRow.setRowDetails(row.getRowHeight(), + row.matchesDefaultFontHeight(), + row.isCollapsed(), + row.getOutlineLevel(), + row.getGroupStart(), + row.getStyle()); + } + } + } + catch (RowsExceededException e) + { + // Handle the rows exceeded exception - this cannot occur since + // the sheet we are copying from will have a valid number of rows + Assert.verify(false); + } + + // Copy the horizontal page breaks + toRowBreaks = new ArrayList(fromRowBreaks); + + // Copy the vertical page breaks + toColumnBreaks = new ArrayList(fromColumnBreaks); + + // Copy the data validations + if (fromDataValidation != null) + { + toDataValidation = new DataValidation + (fromDataValidation, + toSheet.getWorkbook(), + toSheet.getWorkbook(), + toSheet.getWorkbook().getSettings()); + } + + // Copy the charts + sheetWriter.setCharts(fromSheet.getCharts()); + + // Copy the drawings + for (Iterator i = fromDrawings.iterator(); i.hasNext(); ) + { + Object o = i.next(); + if (o instanceof jxl.biff.drawing.Drawing) + { + WritableImage wi = new WritableImage + ((jxl.biff.drawing.Drawing) o, + toSheet.getWorkbook().getDrawingGroup()); + toDrawings.add(wi); + toImages.add(wi); + } + + // Not necessary to copy the comments, as they will be handled by + // the deep copy of the individual cells + } + + // Copy the workspace options + sheetWriter.setWorkspaceOptions(fromWorkspaceOptions); + + // Copy the environment specific print record + if (fromPLSRecord != null) + { + toPLSRecord = new PLSRecord(fromPLSRecord); + } + + // Copy the button property set + if (fromButtonPropertySet != null) + { + toButtonPropertySet = new ButtonPropertySetRecord(fromButtonPropertySet); + } + + // Copy the hyperlinks + for (Iterator i = fromHyperlinks.iterator(); i.hasNext();) + { + WritableHyperlink hr = new WritableHyperlink + ((WritableHyperlink) i.next(), toSheet); + toHyperlinks.add(hr); + } + } + + /** + * Performs a shallow copy of the specified cell + */ + private WritableCell shallowCopyCell(Cell cell) + { + CellType ct = cell.getType(); + WritableCell newCell = null; + + if (ct == CellType.LABEL) + { + newCell = new Label((LabelCell) cell); + } + else if (ct == CellType.NUMBER) + { + newCell = new Number((NumberCell) cell); + } + else if (ct == CellType.DATE) + { + newCell = new DateTime((DateCell) cell); + } + else if (ct == CellType.BOOLEAN) + { + newCell = new Boolean((BooleanCell) cell); + } + else if (ct == CellType.NUMBER_FORMULA) + { + newCell = new ReadNumberFormulaRecord((FormulaData) cell); + } + else if (ct == CellType.STRING_FORMULA) + { + newCell = new ReadStringFormulaRecord((FormulaData) cell); + } + else if( ct == CellType.BOOLEAN_FORMULA) + { + newCell = new ReadBooleanFormulaRecord((FormulaData) cell); + } + else if (ct == CellType.DATE_FORMULA) + { + newCell = new ReadDateFormulaRecord((FormulaData) cell); + } + else if(ct == CellType.FORMULA_ERROR) + { + newCell = new ReadErrorFormulaRecord((FormulaData) cell); + } + else if (ct == CellType.EMPTY) + { + if (cell.getCellFormat() != null) + { + // It is a blank cell, rather than an empty cell, so + // it may have formatting information, so + // it must be copied + newCell = new Blank(cell); + } + } + + return newCell; + } + + /** + * Performs a deep copy of the specified cell, handling the cell format + * + * @param cell the cell to copy + */ + private WritableCell deepCopyCell(Cell cell) + { + WritableCell c = shallowCopyCell(cell); + + if (c == null) + { + return c; + } + + if (c instanceof ReadFormulaRecord) + { + ReadFormulaRecord rfr = (ReadFormulaRecord) c; + boolean crossSheetReference = !rfr.handleImportedCellReferences + (fromSheet.getWorkbook(), + fromSheet.getWorkbook(), + workbookSettings); + + if (crossSheetReference) + { + try + { + logger.warn("Formula " + rfr.getFormula() + + " in cell " + + CellReferenceHelper.getCellReference(cell.getColumn(), + cell.getRow()) + + " cannot be imported because it references another " + + " sheet from the source workbook"); + } + catch (FormulaException e) + { + logger.warn("Formula in cell " + + CellReferenceHelper.getCellReference(cell.getColumn(), + cell.getRow()) + + " cannot be imported: " + e.getMessage()); + } + + // Create a new error formula and add it instead + c = new Formula(cell.getColumn(), cell.getRow(), "\"ERROR\""); + } + } + + // Copy the cell format + CellFormat cf = c.getCellFormat(); + int index = ( (XFRecord) cf).getXFIndex(); + WritableCellFormat wcf = (WritableCellFormat) + xfRecords.get(new Integer(index)); + + if (wcf == null) + { + wcf = copyCellFormat(cf); + } + + c.setCellFormat(wcf); + + return c; + } + + /** + * Perform a shallow copy of the cells from the specified sheet into this one + */ + void shallowCopyCells() + { + // Copy the cells + int cells = fromSheet.getRows(); + Cell[] row = null; + Cell cell = null; + for (int i = 0; i < cells; i++) + { + row = fromSheet.getRow(i); + + for (int j = 0; j < row.length; j++) + { + cell = row[j]; + WritableCell c = shallowCopyCell(cell); + + // Encase the calls to addCell in a try-catch block + // These should not generate any errors, because we are + // copying from an existing spreadsheet. In the event of + // errors, catch the exception and then bomb out with an + // assertion + try + { + if (c != null) + { + toSheet.addCell(c); + } + } + catch (WriteException e) + { + Assert.verify(false); + } + } + } + numRows = toSheet.getRows(); + } + + /** + * Perform a deep copy of the cells from the specified sheet into this one + */ + void deepCopyCells() + { + // Copy the cells + int cells = fromSheet.getRows(); + Cell[] row = null; + Cell cell = null; + for (int i = 0; i < cells; i++) + { + row = fromSheet.getRow(i); + + for (int j = 0; j < row.length; j++) + { + cell = row[j]; + WritableCell c = deepCopyCell(cell); + + // Encase the calls to addCell in a try-catch block + // These should not generate any errors, because we are + // copying from an existing spreadsheet. In the event of + // errors, catch the exception and then bomb out with an + // assertion + try + { + if (c != null) + { + toSheet.addCell(c); + } + } + catch (WriteException e) + { + Assert.verify(false); + } + } + } + } + + /** + * Returns an initialized copy of the cell format + * + * @param cf the cell format to copy + * @return a deep copy of the cell format + */ + private WritableCellFormat copyCellFormat(CellFormat cf) + { + try + { + // just do a deep copy of the cell format for now. This will create + // a copy of the format and font also - in the future this may + // need to be sorted out + XFRecord xfr = (XFRecord) cf; + WritableCellFormat f = new WritableCellFormat(xfr); + formatRecords.addStyle(f); + + // Maintain the local list of formats + int xfIndex = xfr.getXFIndex(); + xfRecords.put(new Integer(xfIndex), f); + + int fontIndex = xfr.getFontIndex(); + fonts.put(new Integer(fontIndex), new Integer(f.getFontIndex())); + + int formatIndex = xfr.getFormatRecord(); + formats.put(new Integer(formatIndex), new Integer(f.getFormatRecord())); + + return f; + } + catch (NumFormatRecordsException e) + { + logger.warn("Maximum number of format records exceeded. Using " + + "default format."); + + return WritableWorkbook.NORMAL_STYLE; + } + } + + + /** + * Accessor for the maximum column outline level + * + * @return the maximum column outline level, or 0 if no outlines/groups + */ + public int getMaxColumnOutlineLevel() + { + return maxColumnOutlineLevel; + } + + /** + * Accessor for the maximum row outline level + * + * @return the maximum row outline level, or 0 if no outlines/groups + */ + public int getMaxRowOutlineLevel() + { + return maxRowOutlineLevel; + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/WritableSheetImpl.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/WritableSheetImpl.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/WritableSheetImpl.java 17 Aug 2012 14:51:12 -0000 1.1 @@ -0,0 +1,2649 @@ +/********************************************************************** +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Iterator; +import java.util.TreeSet; +import java.util.regex.Pattern; + +import common.Assert; +import common.Logger; + +import jxl.BooleanCell; +import jxl.Cell; +import jxl.CellFeatures; +import jxl.CellType; +import jxl.CellView; +import jxl.DateCell; +import jxl.HeaderFooter; +import jxl.Hyperlink; +import jxl.Image; +import jxl.LabelCell; +import jxl.NumberCell; +import jxl.Range; +import jxl.Sheet; +import jxl.SheetSettings; +import jxl.WorkbookSettings; +import jxl.biff.AutoFilter; +import jxl.biff.BuiltInName; +import jxl.biff.CellFinder; +import jxl.biff.CellReferenceHelper; +import jxl.biff.ConditionalFormat; +import jxl.biff.DataValidation; +import jxl.biff.EmptyCell; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.IndexMapping; +import jxl.biff.NumFormatRecordsException; +import jxl.biff.SheetRangeImpl; +import jxl.biff.WorkspaceInformationRecord; +import jxl.biff.XFRecord; +import jxl.biff.drawing.Chart; +import jxl.biff.drawing.ComboBox; +import jxl.biff.drawing.Drawing; +import jxl.biff.drawing.DrawingGroupObject; +import jxl.format.CellFormat; +import jxl.format.Font; +import jxl.format.PageOrientation; +import jxl.format.PaperSize; +import jxl.write.Blank; +import jxl.write.Boolean; +import jxl.write.DateTime; +import jxl.write.Label; +import jxl.write.Number; +import jxl.write.WritableCell; +import jxl.write.WritableCellFormat; +import jxl.write.WritableFont; +import jxl.write.WritableHyperlink; +import jxl.write.WritableImage; +import jxl.write.WritableSheet; +import jxl.write.WritableWorkbook; +import jxl.write.WriteException; + +/** + * A writable sheet. This class contains implementation of all the + * writable sheet methods which may be invoke by the API + */ +class WritableSheetImpl implements WritableSheet +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(WritableSheetImpl.class); + + /** + * The name of this sheet + */ + private String name; + /** + * A handle to the output file which the binary data is written to + */ + private File outputFile; + /** + * The rows within this sheet + */ + private RowRecord[] rows; + /** + * A handle to workbook format records + */ + private FormattingRecords formatRecords; + /** + * A handle to the shared strings used by this workbook + */ + private SharedStrings sharedStrings; + + /** + * The list of non-default column formats + */ + private TreeSet columnFormats; + + /** + * The list of autosized columns + */ + private TreeSet autosizedColumns; + + /** + * The list of hyperlinks + */ + private ArrayList hyperlinks; + + /** + * The list of merged ranged + */ + private MergedCells mergedCells; + + /** + * A number of rows. This is a count of the maximum row number + 1 + */ + private int numRows; + + /** + * The number of columns. This is a count of the maximum column number + 1 + */ + private int numColumns; + + /** + * The environment specific print record, copied from the read spreadsheet + */ + private PLSRecord plsRecord; + + /** + * The buttons property set + */ + private ButtonPropertySetRecord buttonPropertySet; + + /** + * A flag indicating that this sheet is a chart only + */ + private boolean chartOnly; + + /** + * The data validations on this page + */ + private DataValidation dataValidation; + + /** + * Array of row page breaks + */ + private ArrayList rowBreaks; + + /** + * Array of column page breaks + */ + private ArrayList columnBreaks; + + /** + * The drawings on this sheet + */ + private ArrayList drawings; + + /** + * The images on this sheet. This is a subset of the drawings list + */ + private ArrayList images; + + /** + * The conditional formats on this sheet + */ + private ArrayList conditionalFormats; + + /** + * The autofilter + */ + private AutoFilter autoFilter; + + /** + * The writable cells on this sheet which may have validation added + * to them + */ + private ArrayList validatedCells; + + /** + * The combo box object used for list validations on this sheet + */ + private ComboBox comboBox; + + /** + * Drawings modified flag. Set to true if the drawings list has + * been modified + */ + private boolean drawingsModified; + + /** + * The maximum row outline level + */ + private int maxRowOutlineLevel; + + /** + * The maximum column outline level + */ + private int maxColumnOutlineLevel; + + /** + * The settings for this sheet + */ + private SheetSettings settings; + + /** + * The sheet writer engine + */ + private SheetWriter sheetWriter; + + /** + * The settings for the workbook + */ + private WorkbookSettings workbookSettings; + + /** + * The workbook + */ + private WritableWorkbookImpl workbook; + + /** + * The amount by which to grow the rows array + */ + private final static int rowGrowSize = 10; + + /** + * The maximum number of rows excel allows in a worksheet + */ + private final static int numRowsPerSheet = 65536; + + /** + * The maximum number of characters permissible for a sheet name + */ + private final static int maxSheetNameLength = 31; + + /** + * The illegal characters for a sheet name + */ + private final static char[] illegalSheetNameCharacters = + new char[] {'*', ':', '?', '\\'}; + + /** + * The supported file types + */ + private static final String[] imageTypes = new String[] {"png"}; + + /** + * The comparator for column info record + */ + private static class ColumnInfoComparator implements Comparator + { + /** + * Equals method + * + * @param o the object to compare + * @return TRUE if equal, FALSE otherwise + */ + public boolean equals(Object o) + { + return o == this; + } + + /** + * Comparison function for to ColumnInfoRecords + * + * @param o2 first object to compare + * @param o1 second object to compare + * @return the result of the comparison + */ + public int compare(Object o1, Object o2) + { + if (o1 == o2) + { + return 0; + } + + Assert.verify(o1 instanceof ColumnInfoRecord); + Assert.verify(o2 instanceof ColumnInfoRecord); + + ColumnInfoRecord ci1 = (ColumnInfoRecord) o1; + ColumnInfoRecord ci2 = (ColumnInfoRecord) o2; + + return ci1.getColumn() - ci2.getColumn(); + } + } + + /** + * Constructor + * + * @param fr the formatting records used by the workbook + * @param of the output file to write the binary data + * @param f the fonts used by the workbook + * @param n the name of this sheet + * @param ss the shared strings used by the workbook + * @param ws the workbook settings + */ + public WritableSheetImpl(String n, + File of, + FormattingRecords fr, + SharedStrings ss, + WorkbookSettings ws, + WritableWorkbookImpl ww) + { + name = validateName(n); + outputFile = of; + rows = new RowRecord[0]; + numRows = 0; + numColumns = 0; + chartOnly = false; + workbook = ww; + + formatRecords = fr; + sharedStrings = ss; + workbookSettings = ws; + drawingsModified = false; + columnFormats = new TreeSet(new ColumnInfoComparator()); + autosizedColumns = new TreeSet(); + hyperlinks = new ArrayList(); + mergedCells = new MergedCells(this); + rowBreaks = new ArrayList(); + columnBreaks = new ArrayList(); + drawings = new ArrayList(); + images = new ArrayList(); + conditionalFormats = new ArrayList(); + validatedCells = new ArrayList(); + settings = new SheetSettings(this); + + + sheetWriter = new SheetWriter(outputFile, + this, + workbookSettings); + } + + /** + * Returns the cell for the specified location eg. "A4", using the + * CellReferenceHelper + * + * @param loc the cell reference + * @return the cell at the specified co-ordinates + */ + public Cell getCell(String loc) + { + return getCell(CellReferenceHelper.getColumn(loc), + CellReferenceHelper.getRow(loc)); + } + + /** + * Returns the cell specified at this row and at this column + * + * @param column the column number + * @param row the row number + * @return the cell at the specified co-ordinates + */ + public Cell getCell(int column, int row) + { + return getWritableCell(column, row); + } + + /** + * Returns the cell for the specified location eg. "A4". Note that this + * method is identical to calling getCell(CellReferenceHelper.getColumn(loc), + * CellReferenceHelper.getRow(loc)) and its implicit performance + * overhead for string parsing. As such,this method should therefore + * be used sparingly + * + * @param loc the cell reference + * @return the cell at the specified co-ordinates + */ + public WritableCell getWritableCell(String loc) + { + return getWritableCell(CellReferenceHelper.getColumn(loc), + CellReferenceHelper.getRow(loc)); + } + + /** + * Returns the cell specified at this row and at this column + * + * @param column the column number + * @param row the row number + * @return the cell at the specified co-ordinates + */ + public WritableCell getWritableCell(int column, int row) + { + WritableCell c = null; + + if (row < rows.length && rows[row] != null) + { + c = rows[row].getCell(column); + } + + if (c == null) + { + c = new EmptyCell(column, row); + } + + return c; + } + + /** + * Returns the number of rows in this sheet + * + * @return the number of rows in this sheet + */ + public int getRows() + { + return numRows; + } + + /** + * Returns the number of columns in this sheet + * + * @return the number of columns in this sheet + */ + public int getColumns() + { + return numColumns; + } + + /** + * Gets the cell whose contents match the string passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform + * + * @param contents the string to match + * @return the Cell whose contents match the paramter, null if not found + */ + public Cell findCell(String contents) + { + CellFinder cellFinder = new CellFinder(this); + return cellFinder.findCell(contents); + } + + /** + * Gets the cell whose contents match the string passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform + * + * @param contents the string to match + * @param firstCol the first column within the range + * @param firstRow the first row of the range + * @param lastCol the last column within the range + * @param lastRow the last row within the range + * @param reverse indicates whether to perform a reverse search or not + * @return the Cell whose contents match the parameter, null if not found + */ + public Cell findCell(String contents, + int firstCol, + int firstRow, + int lastCol, + int lastRow, + boolean reverse) + { + CellFinder cellFinder = new CellFinder(this); + return cellFinder.findCell(contents, + firstCol, + firstRow, + lastCol, + lastRow, + reverse); + } + + /** + * Gets the cell whose contents match the regular expressionstring passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform + * + * @param pattern the regular expression string to match + * @param firstCol the first column within the range + * @param firstRow the first row of the range + * @param lastRow the last row within the range + * @param lastCol the last column within the ranage + * @param reverse indicates whether to perform a reverse search or not + * @return the Cell whose contents match the parameter, null if not found + */ + public Cell findCell(Pattern pattern, + int firstCol, + int firstRow, + int lastCol, + int lastRow, + boolean reverse) + { + CellFinder cellFinder = new CellFinder(this); + return cellFinder.findCell(pattern, + firstCol, + firstRow, + lastCol, + lastRow, + reverse); + } + + /** + * Gets the cell whose contents match the string passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform. This method differs + * from the findCell methods in that only cells with labels are + * queried - all numerical cells are ignored. This should therefore + * improve performance. + * + * @param contents the string to match + * @return the Cell whose contents match the paramter, null if not found + */ + public LabelCell findLabelCell(String contents) + { + CellFinder cellFinder = new CellFinder(this); + return cellFinder.findLabelCell(contents); + } + + /** + * Gets all the cells on the specified row + * + * @param row the rows whose cells are to be returned + * @return the cells on the given row + */ + public Cell[] getRow(int row) + { + // Find the last non-null cell + boolean found = false; + int col = numColumns - 1; + while (col >= 0 && !found) + { + if (getCell(col, row).getType() != CellType.EMPTY) + { + found = true; + } + else + { + col--; + } + } + + // Only create entries for non-empty cells + Cell[] cells = new Cell[col+1]; + + for (int i = 0; i <= col; i++) + { + cells[i] = getCell(i, row); + } + return cells; + } + + /** + * Gets all the cells on the specified column + * + * @param col the column whose cells are to be returned + * @return the cells on the specified column + */ + public Cell[] getColumn(int col) + { + // Find the last non-null cell + boolean found = false; + int row = numRows - 1; + + while (row >= 0 && !found) + { + if (getCell(col, row).getType() != CellType.EMPTY) + { + found = true; + } + else + { + row--; + } + } + + // Only create entries for non-empty cells + Cell[] cells = new Cell[row+1]; + + for (int i = 0; i <= row; i++) + { + cells[i] = getCell(col, i); + } + return cells; + } + + /** + * Gets the name of this sheet + * + * @return the name of the sheet + */ + public String getName() + { + return name; + } + + /** + * Inserts a blank row into this spreadsheet. If the row is out of range + * of the rows in the sheet, then no action is taken + * + * @param row the row to insert + */ + public void insertRow(int row) + { + if (row < 0 || row >= numRows) + { + return; + } + + // Create a new array to hold the new rows. Grow it if need be + RowRecord[] oldRows = rows; + + if (numRows == rows.length) + { + rows = new RowRecord[oldRows.length + rowGrowSize]; + } + else + { + rows = new RowRecord[oldRows.length]; + } + + // Copy in everything up to the new row + System.arraycopy(oldRows, 0, rows, 0, row); + + // Copy in the remaining rows + System.arraycopy(oldRows, row, rows, row+1, numRows - row); + + // Increment all the internal row number by one + for (int i = row+1; i <= numRows; i++) + { + if (rows[i] != null) + { + rows[i].incrementRow(); + } + } + + // Adjust any hyperlinks + HyperlinkRecord hr = null; + Iterator i = hyperlinks.iterator(); + while (i.hasNext()) + { + hr = (HyperlinkRecord) i.next(); + hr.insertRow(row); + } + + // Adjust any data validations + if (dataValidation != null) + { + dataValidation.insertRow(row); + } + + if (validatedCells != null && validatedCells.size() > 0) + { + for (Iterator vci = validatedCells.iterator(); vci.hasNext();) + { + CellValue cv = (CellValue) vci.next(); + CellFeatures cf = cv.getCellFeatures(); + if (cf.getDVParser() != null) + { + cf.getDVParser().insertRow(row); + } + } + } + + // Adjust any merged cells + mergedCells.insertRow(row); + + // Adjust any page breaks + ArrayList newRowBreaks = new ArrayList(); + Iterator ri = rowBreaks.iterator(); + while (ri.hasNext()) + { + int val = ( (Integer) ri.next()).intValue(); + if (val >= row) + { + val++; + } + + newRowBreaks.add(new Integer(val)); + } + rowBreaks = newRowBreaks; + + // Adjust any conditional formats + for (Iterator cfit = conditionalFormats.iterator(); cfit.hasNext() ;) + { + ConditionalFormat cf = (ConditionalFormat) cfit.next(); + cf.insertRow(row); + } + + // Handle interested cell references on the main workbook + if (workbookSettings.getFormulaAdjust()) + { + workbook.rowInserted(this, row); + } + + // Adjust the maximum row record + numRows++; + } + + /** + * Inserts a blank column into this spreadsheet. If the column is out of + * range of the columns in the sheet, then no action is taken + * + * @param col the column to insert + */ + public void insertColumn(int col) + { + if (col < 0 || col >= numColumns) + { + return; + } + + // Iterate through all the row records adding in the column + for (int i = 0 ; i < numRows ; i++) + { + if (rows[i] != null) + { + rows[i].insertColumn(col); + } + } + + // Adjust any hyperlinks + HyperlinkRecord hr = null; + Iterator i = hyperlinks.iterator(); + while (i.hasNext()) + { + hr = (HyperlinkRecord) i.next(); + hr.insertColumn(col); + } + + // Iterate through the column views, incrementing the column number + i = columnFormats.iterator(); + while (i.hasNext()) + { + ColumnInfoRecord cir = (ColumnInfoRecord) i.next(); + + if (cir.getColumn() >= col) + { + cir.incrementColumn(); + } + } + + // Iterate through the autosized columns, incrementing the column number + if (autosizedColumns.size() > 0) + { + TreeSet newAutosized = new TreeSet(); + i = autosizedColumns.iterator(); + while (i.hasNext()) + { + Integer colnumber = (Integer) i.next(); + + if (colnumber.intValue() >= col) + { + newAutosized.add(new Integer(colnumber.intValue() + 1)); + } + else + { + newAutosized.add(colnumber); + } + } + autosizedColumns = newAutosized; + } + + // Handle any data validations + if (dataValidation != null) + { + dataValidation.insertColumn(col); + } + + if (validatedCells != null && validatedCells.size() > 0) + { + for (Iterator vci = validatedCells.iterator(); vci.hasNext();) + { + CellValue cv = (CellValue) vci.next(); + CellFeatures cf = cv.getCellFeatures(); + if (cf.getDVParser() != null) + { + cf.getDVParser().insertColumn(col); + } + } + } + + // Adjust any merged cells + mergedCells.insertColumn(col); + + // Adjust any page breaks + ArrayList newColumnBreaks = new ArrayList(); + Iterator ri = columnBreaks.iterator(); + while (ri.hasNext()) + { + int val = ( (Integer) ri.next()).intValue(); + if (val >= col) + { + val++; + } + + newColumnBreaks.add(new Integer(val)); + } + columnBreaks = newColumnBreaks; + + // Adjust any conditional formats + for (Iterator cfit = conditionalFormats.iterator(); cfit.hasNext() ;) + { + ConditionalFormat cf = (ConditionalFormat) cfit.next(); + cf.insertColumn(col); + } + + // Handle interested cell references on the main workbook + if (workbookSettings.getFormulaAdjust()) + { + workbook.columnInserted(this, col); + } + + numColumns++; + } + + /** + * Removes a column from this spreadsheet. If the column is out of range + * of the columns in the sheet, then no action is taken + * + * @param col the column to remove + */ + public void removeColumn(int col) + { + if (col < 0 || col >= numColumns) + { + return; + } + + // Iterate through all the row records removing the column + for (int i = 0 ; i < numRows ; i++) + { + if (rows[i] != null) + { + rows[i].removeColumn(col); + } + } + + // Adjust any hyperlinks + HyperlinkRecord hr = null; + Iterator i = hyperlinks.iterator(); + while (i.hasNext()) + { + hr = (HyperlinkRecord) i.next(); + + if (hr.getColumn() == col && + hr.getLastColumn() == col) + { + // The row with the hyperlink on has been removed, so get + // rid of it from the list + i.remove(); + } + else + { + hr.removeColumn(col); + } + } + + // Adjust any data validations + if (dataValidation != null) + { + dataValidation.removeColumn(col); + } + + if (validatedCells != null && validatedCells.size() > 0) + { + for (Iterator vci = validatedCells.iterator(); vci.hasNext();) + { + CellValue cv = (CellValue) vci.next(); + CellFeatures cf = cv.getCellFeatures(); + if (cf.getDVParser() != null) + { + cf.getDVParser().removeColumn(col); + } + } + } + + // Adjust any merged cells + mergedCells.removeColumn(col); + + // Adjust any page breaks + ArrayList newColumnBreaks = new ArrayList(); + Iterator ri = columnBreaks.iterator(); + while (ri.hasNext()) + { + int val = ( (Integer) ri.next()).intValue(); + + if (val != col) + { + if (val > col) + { + val--; + } + + newColumnBreaks.add(new Integer(val)); + } + } + + columnBreaks = newColumnBreaks; + + + // Iterate through the column views, decrementing the column number + i = columnFormats.iterator(); + ColumnInfoRecord removeColumn = null; + while (i.hasNext()) + { + ColumnInfoRecord cir = (ColumnInfoRecord) i.next(); + + if (cir.getColumn() == col) + { + removeColumn = cir; + } + else if (cir.getColumn() > col) + { + cir.decrementColumn(); + } + } + + if (removeColumn != null) + { + columnFormats.remove(removeColumn); + } + + // Iterate through the autosized columns, decrementing the column number + if (autosizedColumns.size() > 0) + { + TreeSet newAutosized = new TreeSet(); + i = autosizedColumns.iterator(); + while (i.hasNext()) + { + Integer colnumber = (Integer) i.next(); + + if (colnumber.intValue() == col) + { + // do nothing + } + else if (colnumber.intValue() > col) + { + newAutosized.add(new Integer(colnumber.intValue() - 1)); + } + else + { + newAutosized.add(colnumber); + } + } + autosizedColumns = newAutosized; + } + + // Adjust any conditional formats + for (Iterator cfit = conditionalFormats.iterator(); cfit.hasNext() ;) + { + ConditionalFormat cf = (ConditionalFormat) cfit.next(); + cf.removeColumn(col); + } + + // Handle interested cell references on the main workbook + if (workbookSettings.getFormulaAdjust()) + { + workbook.columnRemoved(this, col); + } + + numColumns--; + } + + /** + * Removes a row from this spreadsheet. If the row is out of + * range of the columns in the sheet, then no action is taken + * + * @param row the row to remove + */ + public void removeRow(int row) + { + if (row < 0 || row >= numRows) + { + // Call rowRemoved anyway, to adjust the named cells + if (workbookSettings.getFormulaAdjust()) + { + workbook.rowRemoved(this, row); + } + + return; + } + + // Create a new array to hold the new rows. Grow it if need be + RowRecord[] oldRows = rows; + + rows = new RowRecord[oldRows.length]; + + // Copy in everything up to the row to be removed + System.arraycopy(oldRows, 0, rows, 0, row); + + // Copy in the remaining rows + System.arraycopy(oldRows, row + 1, rows, row, numRows - (row + 1)); + + // Decrement all the internal row numbers by one + for (int i = row; i < numRows; i++) + { + if (rows[i] != null) + { + rows[i].decrementRow(); + } + } + + // Adjust any hyperlinks + HyperlinkRecord hr = null; + Iterator i = hyperlinks.iterator(); + while (i.hasNext()) + { + hr = (HyperlinkRecord) i.next(); + + if (hr.getRow() == row && + hr.getLastRow() == row) + { + // The row with the hyperlink on has been removed, so get + // rid of it from the list + i.remove(); + } + else + { + hr.removeRow(row); + } + } + + // Adjust any data validations + if (dataValidation != null) + { + dataValidation.removeRow(row); + } + + if (validatedCells != null && validatedCells.size() > 0) + { + for (Iterator vci = validatedCells.iterator(); vci.hasNext();) + { + CellValue cv = (CellValue) vci.next(); + CellFeatures cf = cv.getCellFeatures(); + if (cf.getDVParser() != null) + { + cf.getDVParser().removeRow(row); + } + } + } + + // Adjust any merged cells + mergedCells.removeRow(row); + + // Adjust any page breaks + ArrayList newRowBreaks = new ArrayList(); + Iterator ri = rowBreaks.iterator(); + while (ri.hasNext()) + { + int val = ( (Integer) ri.next()).intValue(); + + if (val != row) + { + if (val > row) + { + val--; + } + + newRowBreaks.add(new Integer(val)); + } + } + + rowBreaks = newRowBreaks; + + // Adjust any conditional formats + for (Iterator cfit = conditionalFormats.iterator(); cfit.hasNext() ;) + { + ConditionalFormat cf = (ConditionalFormat) cfit.next(); + cf.removeRow(row); + } + + // Handle interested cell references on the main workbook + if (workbookSettings.getFormulaAdjust()) + { + workbook.rowRemoved(this, row); + } + + // Adjust any drawings + /* + if (drawings != null) + { + for (Iterator drawingIt = drawings.iterator() ; drawingIt.hasNext() ; ) + { + DrawingGroupObject dgo = (DrawingGroupObject) drawingIt.next(); + dgo.removeRow(row); + } + } + */ + + // Adjust the maximum row record + numRows--; + } + + /** + * Adds the cell to this sheet. If the cell has already been added to + * this sheet or another sheet, a WriteException is thrown. If the + * position to be occupied by this cell is already taken, the incumbent + * cell is replaced. + * The cell is then marked as referenced, and its formatting information + * registered with the list of formatting records updated if necessary + * The RowsExceededException may be caught if client code wishes to + * explicitly trap the case where too many rows have been written + * to the current sheet. If this behaviour is not desired, it is + * sufficient simply to handle the WriteException, since this is a base + * class of RowsExceededException + * + * @exception WriteException + * @exception RowsExceededException + * @param cell the cell to add + */ + public void addCell(WritableCell cell) + throws WriteException, RowsExceededException + { + if (cell.getType() == CellType.EMPTY) + { + if (cell != null && cell.getCellFormat() == null) + { + // return if it's a blank cell with no particular cell formatting + // information + return; + } + } + + CellValue cv = (CellValue) cell; + + if (cv.isReferenced()) + { + throw new JxlWriteException(JxlWriteException.cellReferenced); + } + + int row = cell.getRow(); + RowRecord rowrec = getRowRecord(row); + rowrec.addCell(cv); + + // Adjust the max rows and max columns accordingly + numRows = Math.max(row+1, numRows); + numColumns = Math.max(numColumns, rowrec.getMaxColumn()); + + // Indicate this cell is now part of a worksheet, so that it can't be + // added anywhere else + cv.setCellDetails(formatRecords, sharedStrings, this); + } + + /** + * Gets the row record at the specified row number, growing the + * array as needs dictate + * + * @param row the row number we are interested in + * @return the row record at the specified row + * @exception RowsExceededException + */ + RowRecord getRowRecord(int row) throws RowsExceededException + { + if (row >= numRowsPerSheet) + { + throw new RowsExceededException(); + } + + // Grow the array of rows if needs be + // Thanks to Brendan for spotting the flaw in merely adding on the + // grow size + if (row >= rows.length) + { + RowRecord[] oldRows = rows; + rows = new RowRecord[Math.max(oldRows.length + rowGrowSize, row+1)]; + System.arraycopy(oldRows, 0, rows, 0, oldRows.length); + oldRows = null; + } + + RowRecord rowrec = rows[row]; + + if (rowrec == null) + { + rowrec = new RowRecord(row, this); + rows[row] = rowrec; + } + + return rowrec; + } + + /** + * Gets the row record for the specified row + * + * @param r the row + * @return the row record + */ + RowRecord getRowInfo(int r) + { + if (r < 0 || r > rows.length) + { + return null; + } + + return rows[r]; + } + + /** + * Gets the column info record for the specified column + * + * @param c the column + * @return the column record + */ + ColumnInfoRecord getColumnInfo(int c) + { + Iterator i = columnFormats.iterator(); + ColumnInfoRecord cir = null; + boolean stop = false; + + while (i.hasNext() && !stop) + { + cir = (ColumnInfoRecord) i.next(); + + if (cir.getColumn() >= c) + { + stop = true; + } + } + + if (!stop) + { + return null; + } + + return cir.getColumn() == c ? cir : null; + } + + /** + * Sets the name of this worksheet + * + * @param n the name of this sheet + */ + public void setName(String n) + { + name = n; + } + + /** + * Sets the hidden status of this sheet + * + * @param h the hiden flag + * @deprecated Use the settings bean instead + */ + public void setHidden(boolean h) + { + settings.setHidden(h); + } + + /** + * Indicates whether or not this sheet is protected + * + * @param prot protected flag + * @deprecated Use the settings bean instead + */ + public void setProtected(boolean prot) + { + settings.setProtected(prot); + } + + /** + * Sets this sheet as selected + * @deprecated Use the settings bean + */ + public void setSelected() + { + settings.setSelected(); + } + + /** + * Retrieves the hidden status of this sheet + * + * @return TRUE if hidden, FALSE otherwise + * @deprecated Use the sheet settings bean instead + */ + public boolean isHidden() + { + return settings.isHidden(); + } + + /** + * Sets the width (in characters) for a particular column in this sheet + * + * @param col the column whose width to set + * @param width the width of the column in characters + */ + public void setColumnView(int col, int width) + { + CellView cv = new CellView(); + cv.setSize(width * 256); + setColumnView(col, cv); + } + + /** + * Sets the width (in characters) and format options for a + * particular column in this sheet + * + * @param col the column to set + * @param width the width in characters + * @param format the formt details for the column + */ + public void setColumnView(int col, int width, CellFormat format) + { + CellView cv = new CellView(); + cv.setSize(width * 256); + cv.setFormat(format); + setColumnView(col, cv); + } + + /** + * Sets the view for this column + * + * @param col the column on which to set the view + * @param view the view to set + */ + public void setColumnView(int col, CellView view) + { + XFRecord xfr = (XFRecord) view.getFormat(); + if (xfr == null) + { + Styles styles = getWorkbook().getStyles(); + xfr = styles.getNormalStyle(); + } + + try + { + if (!xfr.isInitialized()) + { + formatRecords.addStyle(xfr); + } + + int width = view.depUsed() ? view.getDimension() * 256 : view.getSize(); + + if (view.isAutosize()) + { + autosizedColumns.add(new Integer(col)); + } + + ColumnInfoRecord cir = new ColumnInfoRecord(col, + width, + xfr); + + if (view.isHidden()) + { + cir.setHidden(true); + } + + if (!columnFormats.contains(cir)) + { + columnFormats.add(cir); + } + else + { + columnFormats.remove(cir); + columnFormats.add(cir); + } + } + catch (NumFormatRecordsException e) + { + logger.warn("Maximum number of format records exceeded. Using " + + "default format."); + + ColumnInfoRecord cir = new ColumnInfoRecord + (col, view.getDimension()*256, WritableWorkbook.NORMAL_STYLE); + if (!columnFormats.contains(cir)) + { + columnFormats.add(cir); + } + } + } + + + /** + * Sets the height of the specified row, as well as its collapse status + * + * @param row the row to be formatted + * @param height the row height in 1/20ths of a point + * @exception RowsExceededException + * @deprecated use the override which takes a CellView object + */ + public void setRowView(int row, int height) throws RowsExceededException + { + CellView cv = new CellView(); + cv.setSize(height); + cv.setHidden(false); + setRowView(row, cv); + } + + /** + * Sets the height of the specified row, as well as its collapse status + * + * @param row the row to be formatted + * @param collapsed indicates whether the row is collapsed + * @exception jxl.write.biff.RowsExceededException + * @deprecated use the override which takes a CellView object + */ + public void setRowView(int row, boolean collapsed) + throws RowsExceededException + { + CellView cv = new CellView(); + cv.setHidden(collapsed); + setRowView(row, cv); + } + + /** + * Sets the height of the specified row, as well as its collapse status + * + * @param row the row to be formatted + * @param height the row height in 1/20th of a point + * @param collapsed indicates whether the row is collapsed + * @param zeroHeight indicates that the row has zero height + * @exception RowsExceededException + * @deprecated use the override which takes a CellView object + */ + public void setRowView(int row, int height, + boolean collapsed) + throws RowsExceededException + { + CellView cv = new CellView(); + cv.setSize(height); + cv.setHidden(collapsed); + setRowView(row, cv); + } + + /** + * Sets the view for this column + * + * @param row the column on which to set the view + * @param view the view to set + * @exception RowsExceededException + */ + public void setRowView(int row, CellView view) throws RowsExceededException + { + RowRecord rowrec = getRowRecord(row); + + XFRecord xfr = (XFRecord) view.getFormat(); + + try + { + if (xfr != null) + { + if (!xfr.isInitialized()) + { + formatRecords.addStyle(xfr); + } + } + } + catch (NumFormatRecordsException e) + { + logger.warn("Maximum number of format records exceeded. Using " + + "default format."); + + xfr = null; + } + + rowrec.setRowDetails(view.getSize(), + false, + view.isHidden(), + 0, + false, + xfr); + numRows = Math.max(numRows, row + 1); + } + + /** + * Writes out this sheet. This functionality is delegated off to the + * SheetWriter class in order to reduce the bloated nature of this source + * file + * + * @exception IOException + */ + public void write() throws IOException + { + boolean dmod = drawingsModified; + if (workbook.getDrawingGroup() != null) + { + dmod |= workbook.getDrawingGroup().hasDrawingsOmitted(); + } + + if (autosizedColumns.size() > 0) + { + autosizeColumns(); + } + + sheetWriter.setWriteData(rows, + rowBreaks, + columnBreaks, + hyperlinks, + mergedCells, + columnFormats, + maxRowOutlineLevel, + maxColumnOutlineLevel); + sheetWriter.setDimensions(getRows(), getColumns()); + sheetWriter.setSettings(settings); + sheetWriter.setPLS(plsRecord); + sheetWriter.setDrawings(drawings, dmod); + sheetWriter.setButtonPropertySet(buttonPropertySet); + sheetWriter.setDataValidation(dataValidation, validatedCells); + sheetWriter.setConditionalFormats(conditionalFormats); + sheetWriter.setAutoFilter(autoFilter); + + sheetWriter.write(); + } + + /** + * Copies the specified sheet, row by row and cell by cell + * + * @param s the sheet to copy + */ + void copy(Sheet s) + { + // Copy the settings + settings = new SheetSettings(s.getSettings(), this); + + SheetCopier si = new SheetCopier(s, this); + si.setColumnFormats(columnFormats); + si.setFormatRecords(formatRecords); + si.setHyperlinks(hyperlinks); + si.setMergedCells(mergedCells); + si.setRowBreaks(rowBreaks); + si.setColumnBreaks(columnBreaks); + si.setSheetWriter(sheetWriter); + si.setDrawings(drawings); + si.setImages(images); + si.setConditionalFormats(conditionalFormats); + + si.copySheet(); + + dataValidation = si.getDataValidation(); + comboBox = si.getComboBox(); + plsRecord = si.getPLSRecord(); + chartOnly = si.isChartOnly(); + buttonPropertySet = si.getButtonPropertySet(); + numRows = si.getRows(); + autoFilter = si.getAutoFilter(); + maxRowOutlineLevel = si.getMaxRowOutlineLevel(); + maxColumnOutlineLevel = si.getMaxColumnOutlineLevel(); + } + + /** + * Copies the specified sheet, row by row and cell by cell + * + * @param s the sheet to copy + */ + void copy(WritableSheet s) + { + settings = new SheetSettings(s.getSettings(), this); + WritableSheetImpl si = (WritableSheetImpl) s; + + WritableSheetCopier sc = new WritableSheetCopier(s, this); + sc.setColumnFormats(si.columnFormats, columnFormats); + sc.setMergedCells(si.mergedCells, mergedCells); + sc.setRows(si.rows); + sc.setRowBreaks(si.rowBreaks, rowBreaks); + sc.setColumnBreaks(si.columnBreaks, columnBreaks); + sc.setDataValidation(si.dataValidation); + sc.setSheetWriter(sheetWriter); + sc.setDrawings(si.drawings, drawings, images); + sc.setWorkspaceOptions(si.getWorkspaceOptions()); + sc.setPLSRecord(si.plsRecord); + sc.setButtonPropertySetRecord(si.buttonPropertySet); + sc.setHyperlinks(si.hyperlinks, hyperlinks); + + sc.copySheet(); + + dataValidation = sc.getDataValidation(); + plsRecord = sc.getPLSRecord(); + buttonPropertySet = sc.getButtonPropertySet(); + } + + /** + * Gets the header. Called when copying sheets + * + * @return the page header + */ + final HeaderRecord getHeader() + { + return sheetWriter.getHeader(); + } + + /** + * Gets the footer. Called when copying sheets + * + * @return the page footer + */ + final FooterRecord getFooter() + { + return sheetWriter.getFooter(); + } + /** + * Determines whether the sheet is protected + * + * @return whether or not the sheet is protected + * @deprecated Use the SheetSettings bean instead + */ + public boolean isProtected() + { + return settings.isProtected(); + } + + /** + * Gets the hyperlinks on this sheet + * + * @return an array of hyperlinks + */ + public Hyperlink[] getHyperlinks() + { + Hyperlink[] hl = new Hyperlink[hyperlinks.size()]; + + for (int i = 0; i < hyperlinks.size(); i++) + { + hl[i] = (Hyperlink) hyperlinks.get(i); + } + + return hl; + } + + /** + * Gets the cells which have been merged on this sheet + * + * @return an array of range objects + */ + public Range[] getMergedCells() + { + return mergedCells.getMergedCells(); + } + + /** + * Gets the writable hyperlinks on this sheet + * + * @return an array of hyperlinks + */ + public WritableHyperlink[] getWritableHyperlinks() + { + WritableHyperlink[] hl = new WritableHyperlink[hyperlinks.size()]; + + for (int i = 0; i < hyperlinks.size(); i++) + { + hl[i] = (WritableHyperlink) hyperlinks.get(i); + } + + return hl; + } + + /** + * Removes the specified hyperlink. Note that if you merely set the + * cell contents to be an Empty cell, then the cells containing the + * hyperlink will still be active. The contents of the cell which + * activate the hyperlink are removed. + * The hyperlink passed in must be a hyperlink retrieved using the + * getHyperlinks method + * + * @param h the hyperlink to remove. + * @param preserveLabel if TRUE preserves the label contents, if FALSE + * removes them + */ + public void removeHyperlink(WritableHyperlink h) + { + removeHyperlink(h, false); + } + + /** + * Removes the specified hyperlink. Note that if you merely set the + * cell contents to be an Empty cell, then the cells containing the + * hyperlink will still be active. + * If the preserveLabel field is set, the cell contents of the + * hyperlink are preserved, although the hyperlink is deactivated. If + * this value is FALSE, the cell contents are removed + * The hyperlink passed in must be a hyperlink retrieved using the + * getHyperlinks method + * + * @param h the hyperlink to remove. + * @param preserveLabel if TRUE preserves the label contents, if FALSE + * removes them + */ + public void removeHyperlink(WritableHyperlink h, boolean preserveLabel) + { + // Remove the hyperlink + hyperlinks.remove(hyperlinks.indexOf(h)); + + if (!preserveLabel) + { + // Set the cell contents for the hyperlink - including any formatting + // information - to be empty + Assert.verify(rows.length > h.getRow() && rows[h.getRow()] != null); + rows[h.getRow()].removeCell(h.getColumn()); + } + } + + /** + * Adds the specified hyperlink + * + * @param the hyperlink + * @exception WriteException + * @exception RowsExceededException + */ + public void addHyperlink(WritableHyperlink h) + throws WriteException, RowsExceededException + { + // First set the label on the sheet + Cell c = getCell(h.getColumn(), h.getRow()); + + String contents = null; + if (h.isFile() || h.isUNC()) + { + String cnts = ( (HyperlinkRecord) h).getContents(); + if (cnts == null) + { + contents = h.getFile().getPath(); + } + else + { + contents = cnts; + } + } + else if (h.isURL()) + { + String cnts = ( (HyperlinkRecord) h).getContents(); + if (cnts == null) + { + contents = h.getURL().toString(); + } + else + { + contents=cnts; + } + } + else if (h.isLocation()) + { + contents = ( (HyperlinkRecord) h).getContents(); + } + + // If the cell type is a label, then preserve the cell contents + // and most of the format (apart from the font) + // otherwise overwrite the cell content and the format with the contents + // and the standard hyperlink format + if (c.getType() == CellType.LABEL) + { + Label l = (Label) c; + l.setString(contents); + WritableCellFormat wcf = new WritableCellFormat(l.getCellFormat()); + ( (XFRecord) wcf).setFont(WritableWorkbook.HYPERLINK_FONT); + l.setCellFormat(wcf); + } + else + { + Label l = new Label(h.getColumn(), h.getRow(), contents, + WritableWorkbook.HYPERLINK_STYLE); + addCell(l); + } + + // Set all other cells within range to be empty + for (int i = h.getRow(); i <= h.getLastRow(); i++) + { + for (int j = h.getColumn(); j <= h.getLastColumn(); j++) + { + if (i != h.getRow() && j != h.getColumn()) + { + // Set the cell to be empty + if (rows.length < h.getLastColumn() && rows[i] != null) + { + rows[i].removeCell(j); + } + } + } + } + + ((HyperlinkRecord) h).initialize(this); + hyperlinks.add(h); + } + + /** + * Merges the specified cells. Any clashes or intersections between + * merged cells are resolved when the spreadsheet is written out + * + * @param col1 the column number of the top left cell + * @param row1 the row number of the top left cell + * @param col2 the column number of the bottom right cell + * @param row2 the row number of the bottom right cell + * @return the Range object representing the merged cells + * @exception jxl.write..WriteException + * @exception jxl.write.biff.RowsExceededException + */ + public Range mergeCells(int col1, int row1, int col2, int row2) + throws WriteException, RowsExceededException + { + // First check that the cells make sense + if (col2 < col1 || row2 < row1) + { + logger.warn("Cannot merge cells - top left and bottom right "+ + "incorrectly specified"); + } + + // Make sure the spreadsheet is up to size + if (col2 >= numColumns || row2 >= numRows) + { + addCell(new Blank(col2, row2)); + } + + SheetRangeImpl range = new SheetRangeImpl(this, col1, row1, col2, row2); + mergedCells.add(range); + + return range; + } + + /** + * Sets a row grouping + * + * @param row1 the first row of the group + * @param row2 the last row of the group + * @param collapsed should the group be collapsed? + * @exception WriteException + * @exception RowsExceededException + */ + public void setRowGroup(int row1, int row2, + boolean collapsed) + throws WriteException, RowsExceededException + { + if (row2 < row1) + { + logger.warn("Cannot merge cells - top and bottom rows incorrectly " + + "specified"); + } + + for (int i = row1; i <= row2; i++) + { + RowRecord row = getRowRecord(i); + numRows = Math.max(i+1, numRows); + row.incrementOutlineLevel(); + row.setCollapsed(collapsed); + maxRowOutlineLevel = Math.max(maxRowOutlineLevel, + row.getOutlineLevel()); + } + } + + /** + * Unsets a row grouping + * + * @param row1 the first row to unset + * @param row2 the last row to unset + * @exception WriteException + * @exception RowsExceededException + */ + public void unsetRowGroup(int row1, int row2) + throws WriteException, RowsExceededException + { + if (row2 < row1) + { + logger.warn("Cannot merge cells - top and bottom rows incorrectly " + + "specified"); + } + + // Make sure the spreadsheet is up to size + if (row2 >= numRows) + { + logger.warn("" + row2 + + " is greater than the sheet bounds"); + row2 = numRows - 1; + } + + for (int i = row1; i <= row2; i++) + { + rows[i].decrementOutlineLevel(); + } + + // Recalculate the max outline level + maxRowOutlineLevel = 0; + for (int i = rows.length; i-- > 0; ) + { + maxRowOutlineLevel = Math.max(maxRowOutlineLevel, + rows[i].getOutlineLevel()); + } + } + + /** + * Sets a column grouping + * + * @param col1 the first column of the group + * @param col2 the last column of the group + * @param collapsed should the group be collapsed? + * @exception WriteException + * @exception RowsExceededException + */ + public void setColumnGroup(int col1, int col2, boolean collapsed) + throws WriteException, RowsExceededException + { + if (col2 < col1) + { + logger.warn("Cannot merge cells - top and bottom rows incorrectly " + + "specified"); + } + + for (int i = col1; i <= col2; i++) + { + ColumnInfoRecord cir = getColumnInfo(i); + + // Create the column info record if not present using a default + // cell view + if (cir == null) + { + setColumnView(i, new CellView()); + cir = getColumnInfo(i); + } + + cir.incrementOutlineLevel(); + cir.setCollapsed(collapsed); + maxColumnOutlineLevel = Math.max(maxColumnOutlineLevel, + cir.getOutlineLevel()); + } + } + + /** + * Unsets a column grouping + * + * @param col1 the first column to unset + * @param col2 the last column to unset + * @exception WriteException + * @exception RowsExceededException + */ + public void unsetColumnGroup(int col1, int col2) + throws WriteException, RowsExceededException + { + if (col2 < col1) + { + logger.warn("Cannot merge cells - top and bottom rows incorrectly " + + "specified"); + } + + for (int i = col1; i <= col2; i++) + { + ColumnInfoRecord cir = getColumnInfo(i); + cir.decrementOutlineLevel(); + } + + // Recalculate the max outline level + maxColumnOutlineLevel = 0; + for (Iterator it = columnFormats.iterator(); it.hasNext(); ) + { + ColumnInfoRecord cir = (ColumnInfoRecord)it.next(); + maxColumnOutlineLevel = Math.max(maxColumnOutlineLevel, + cir.getOutlineLevel()); + } + } + + /** + * Unmerges the specified cells. The Range passed in should be one that + * has been previously returned as a result of the getMergedCells method + * + * @param r the range of cells to unmerge + */ + public void unmergeCells(Range r) + { + mergedCells.unmergeCells(r); + } + + /** + * Sets the header for this page + * + * @param l the print header to print on the left side + * @param c the print header to print in the centre + * @param r the print header to print on the right hand side + * @deprecated Use the sheet settings bean + */ + public void setHeader(String l, String c, String r) + { + HeaderFooter header = new HeaderFooter(); + header.getLeft().append(l); + header.getCentre().append(c); + header.getRight().append(r); + settings.setHeader(header); + } + + /** + * Sets the footer for this page + * + * @param l the print header to print on the left side + * @param c the print header to print in the centre + * @param r the print header to print on the right hand side + * @deprecated Use the sheet settings bean + */ + public void setFooter(String l, String c, String r) + { + HeaderFooter footer = new HeaderFooter(); + footer.getLeft().append(l); + footer.getCentre().append(c); + footer.getRight().append(r); + settings.setFooter(footer); + } + + /** + * Sets the page setup details + * + * @param p the page orientation + * @deprecated Use the SheetSettings bean + */ + public void setPageSetup(PageOrientation p) + { + settings.setOrientation(p); + } + + /** + * Sets the page setup details + * + * @param p the page orientation + * @param hm the header margin, in inches + * @param fm the footer margin, in inches + * @deprecated Use the SheetSettings bean + */ + public void setPageSetup(PageOrientation p, double hm, double fm) + { + settings.setOrientation(p); + settings.setHeaderMargin(hm); + settings.setFooterMargin(fm); + } + + /** + * Sets the page setup details + * + * @param p the page orientation + * @param ps the paper size + * @param hm the header margin, in inches + * @param fm the footer margin, in inches + * @deprecated Use the SheetSettings bean + */ + public void setPageSetup(PageOrientation p, PaperSize ps, + double hm, double fm) + { + settings.setPaperSize(ps); + settings.setOrientation(p); + settings.setHeaderMargin(hm); + settings.setFooterMargin(fm); + } + + /** + * Gets the settings for this sheet + * + * @return the page settings bean + */ + public SheetSettings getSettings() + { + return settings; + } + + /** + * Gets the workbook settings + */ + WorkbookSettings getWorkbookSettings() + { + return workbookSettings; + } + + /** + * Forces a page break at the specified row + * + * @param row the row to break at + */ + public void addRowPageBreak(int row) + { + // First check that the row is not already present + Iterator i = rowBreaks.iterator(); + boolean found = false; + + while (i.hasNext() && !found) + { + if (( (Integer) i.next()).intValue() == row) + { + found = true; + } + } + + if (!found) + { + rowBreaks.add(new Integer(row)); + } + } + + /** + * Forces a page break at the specified column + * + * @param col the column to break at + */ + public void addColumnPageBreak(int col) + { + // First check that the row is not already present + Iterator i = columnBreaks.iterator(); + boolean found = false; + + while (i.hasNext() && !found) + { + if (( (Integer) i.next()).intValue() == col) + { + found = true; + } + } + + if (!found) + { + columnBreaks.add(new Integer(col)); + } + } + + /** + * Accessor for the charts. Used when copying + * + * @return the charts on this sheet + */ + Chart[] getCharts() + { + return sheetWriter.getCharts(); + } + + /** + * Accessor for the drawings. Used when copying + * + * @return the drawings on this sheet + */ + private DrawingGroupObject[] getDrawings() + { + DrawingGroupObject[] dr = new DrawingGroupObject[drawings.size()]; + dr = (DrawingGroupObject[]) drawings.toArray(dr); + return dr; + } + + /** + * Check all the merged cells for borders. Although in an OO sense the + * logic should belong in this class, in order to reduce the bloated + * nature of the source code for this object this logic has been delegated + * to the SheetWriter + */ + void checkMergedBorders() + { + sheetWriter.setWriteData(rows, + rowBreaks, + columnBreaks, + hyperlinks, + mergedCells, + columnFormats, + maxRowOutlineLevel, + maxColumnOutlineLevel); + sheetWriter.setDimensions(getRows(), getColumns()); + sheetWriter.checkMergedBorders(); + } + + /** + * Accessor for the workspace options + * + * @return the workspace options + */ + private WorkspaceInformationRecord getWorkspaceOptions() + { + return sheetWriter.getWorkspaceOptions(); + } + + /** + * Rationalizes the sheets xf index mapping + * @param xfMapping the index mapping for XFRecords + * @param fontMapping the index mapping for fonts + * @param formatMapping the index mapping for formats + */ + void rationalize(IndexMapping xfMapping, + IndexMapping fontMapping, + IndexMapping formatMapping) + { + // Rationalize the column formats + for (Iterator i = columnFormats.iterator() ; i.hasNext() ;) + { + ColumnInfoRecord cir = (ColumnInfoRecord) i.next(); + cir.rationalize(xfMapping); + } + + // Rationalize the row formats + for (int i = 0; i < rows.length ; i++) + { + if (rows[i] != null) + { + rows[i].rationalize(xfMapping); + } + } + + // Rationalize any data that appears on the charts + Chart[] charts = getCharts(); + for (int c = 0; c < charts.length; c++) + { + charts[c].rationalize(xfMapping, fontMapping, formatMapping); + } + } + + /** + * Accessor for the workbook + * @return the workbook + */ + WritableWorkbookImpl getWorkbook() + { + return workbook; + } + + /** + * Gets the column format for the specified column + * + * @param col the column number + * @return the column format, or NULL if the column has no specific format + * @deprecated Use getColumnView instead + */ + public CellFormat getColumnFormat(int col) + { + return getColumnView(col).getFormat(); + } + + /** + * Gets the column width for the specified column + * + * @param col the column number + * @return the column width, or the default width if the column has no + * specified format + * @deprecated Use getColumnView instead + */ + public int getColumnWidth(int col) + { + return getColumnView(col).getDimension(); + } + + /** + * Gets the column width for the specified column + * + * @param row the column number + * @return the row height, or the default height if the column has no + * specified format + * @deprecated Use getRowView instead + */ + public int getRowHeight(int row) + { + return getRowView(row).getDimension(); + } + + /** + * Accessor for the chart only method + * + * @return TRUE if this is a chart only, FALSE otherwise + */ + boolean isChartOnly() + { + return chartOnly; + } + + /** + * Gets the row view for the specified row + * + * @param col the row number + * @return the row format, or the default format if no override is + specified + */ + public CellView getRowView(int row) + { + CellView cv = new CellView(); + + try + { + RowRecord rr = getRowRecord(row); + + if (rr == null || rr.isDefaultHeight()) + { + cv.setDimension(settings.getDefaultRowHeight()); + cv.setSize(settings.getDefaultRowHeight()); + } + else if (rr.isCollapsed()) + { + cv.setHidden(true); + } + else + { + cv.setDimension(rr.getRowHeight()); + cv.setSize(rr.getRowHeight()); + } + return cv; + } + catch (RowsExceededException e) + { + // Simple return the default + cv.setDimension(settings.getDefaultRowHeight()); + cv.setSize(settings.getDefaultRowHeight()); + return cv; + } + } + + /** + * Gets the column width for the specified column + * + * @param col the column number + * @return the column format, or the default format if no override is + specified + */ + public CellView getColumnView(int col) + { + ColumnInfoRecord cir = getColumnInfo(col); + CellView cv = new CellView(); + + if (cir != null) + { + cv.setDimension(cir.getWidth()/256); + cv.setSize(cir.getWidth()); + cv.setHidden(cir.getHidden()); + cv.setFormat(cir.getCellFormat()); + } + else + { + cv.setDimension(settings.getDefaultColumnWidth()/256); + cv.setSize(settings.getDefaultColumnWidth() * 256); + } + + return cv; + } + + /** + * Adds an image to this sheet + * + * @param image the image to add + */ + public void addImage(WritableImage image) + { + boolean supported = false; + java.io.File imageFile = image.getImageFile(); + String fileType = "?"; + + if (imageFile != null) + { + String fileName = imageFile.getName(); + int fileTypeIndex = fileName.lastIndexOf('.'); + fileType = fileTypeIndex != -1 ? + fileName.substring(fileTypeIndex+1) : ""; + + for (int i = 0 ; i < imageTypes.length && !supported ; i++) + { + if (fileType.equalsIgnoreCase(imageTypes[i])) + { + supported = true; + } + } + } + else + { + supported = true; + } + + if (supported) + { + workbook.addDrawing(image); + drawings.add(image); + images.add(image); + } + else + { + StringBuffer message = new StringBuffer("Image type "); + message.append(fileType); + message.append(" not supported. Supported types are "); + message.append(imageTypes[0]); + for (int i = 1 ; i < imageTypes.length ; i++) + { + message.append(", "); + message.append(imageTypes[i]); + } + logger.warn(message.toString()); + } + } + + /** + * Gets the number of images on this sheet + * + * @return the number of images on this sheet + */ + public int getNumberOfImages() + { + return images.size(); + } + + /** + * Accessor for a particular image on this sheet + * + * @param i the 0-based image index number + * @return the image with the specified index number + */ + public WritableImage getImage(int i) + { + return (WritableImage) images.get(i); + } + + /** + * Accessor for a particular image on this sheet + * + * @param i the 0-based image index number + * @return the image with the specified index number + */ + public Image getDrawing(int i) + { + return (Image) images.get(i); + } + + /** + * Removes the specified image from this sheet. The image passed in + * must be the same instance as that retrieved from a getImage call + * + * @param wi the image to remove + */ + public void removeImage(WritableImage wi) + { + drawings.remove(wi); + images.remove(wi); + drawingsModified = true; + workbook.removeDrawing(wi); + } + + /** + * Validates the sheet name + */ + private String validateName(String n) + { + if (n.length() > maxSheetNameLength) + { + logger.warn("Sheet name " + n + " too long - truncating"); + n = n.substring(0, maxSheetNameLength); + } + + if (n.charAt(0) == '\'') + { + logger.warn("Sheet naming cannot start with \' - removing"); + n = n.substring(1); + } + + for (int i = 0 ; i < illegalSheetNameCharacters.length ; i++) + { + String newname = n.replace(illegalSheetNameCharacters[i], '@'); + if (n != newname) + { + logger.warn(illegalSheetNameCharacters[i] + + " is not a valid character within a sheet name - replacing"); + } + n = newname; + } + + return n; + } + + /** + * Adds a drawing to the list - typically used for comments + * + * @param the drawing to add + */ + void addDrawing(DrawingGroupObject o) + { + drawings.add(o); + Assert.verify(!(o instanceof Drawing)); + } + + /** + * Removes a drawing to the list - typically used for comments + * + * @param the drawing to add + */ + void removeDrawing(DrawingGroupObject o) + { + int origSize = drawings.size(); + drawings.remove(o); + int newSize = drawings.size(); + drawingsModified = true; + Assert.verify(newSize == origSize -1); + } + + /** + * Removes the data validation for the specified cell. Called from + * CellValue in response to a cell being replaced + * + * @param cv the cell being removed + */ + void removeDataValidation(CellValue cv) + { + dataValidation.removeDataValidation(cv.getColumn(), cv.getRow()); + } + + /** + * Accessor for the page breaks on this sheet + * + * @return the page breaks on this sheet + */ + public int[] getRowPageBreaks() + { + int[] rb = new int[rowBreaks.size()]; + int pos = 0; + for (Iterator i = rowBreaks.iterator(); i.hasNext() ; pos++) + { + rb[pos] = ( (Integer) i.next()).intValue(); + } + return rb; + } + + /** + * Accessor for the page breaks on this sheet + * + * @return the page breaks on this sheet + */ + public int[] getColumnPageBreaks() + { + int[] rb = new int[columnBreaks.size()]; + int pos = 0; + for (Iterator i = columnBreaks.iterator(); i.hasNext() ; pos++) + { + rb[pos] = ( (Integer) i.next()).intValue(); + } + return rb; + } + + /** + * Flags the added cell as having data validation + * + * @param cell the cell with data validation + */ + void addValidationCell(CellValue cv) + { + validatedCells.add(cv); + } + + /** + * Accessor for the combo box object used for list data validations on this + * sheet + * + * @return the combo box + */ + ComboBox getComboBox() + { + return comboBox; + } + + /** + * Sets the combo box object used for list validations on this sheet + * + * @param cb the combo box + */ + void setComboBox(ComboBox cb) + { + comboBox = cb; + } + + /** + * Gets the data validation. Retrieved by CellValue when copying sheets + */ + public DataValidation getDataValidation() + { + return dataValidation; + } + + /** + * Performs the column autosizing + */ + private void autosizeColumns() + { + Iterator i = autosizedColumns.iterator(); + while (i.hasNext()) + { + Integer col = (Integer) i.next(); + autosizeColumn(col.intValue()); + } + } + + /** + * Autosizes the specified column + * + * @param col the column to autosize + */ + private void autosizeColumn(int col) + { + int maxWidth = 0; + ColumnInfoRecord cir = getColumnInfo(col); + Font columnFont = cir.getCellFormat().getFont(); + Font defaultFont = WritableWorkbook.NORMAL_STYLE.getFont(); + + for (int i = 0 ; i < numRows; i++) + { + Cell cell = null; + if (rows[i] != null) + { + cell = rows[i].getCell(col); + } + + if (cell != null) + { + String contents = cell.getContents(); + Font font = cell.getCellFormat().getFont(); + + Font activeFont = font.equals(defaultFont) ? columnFont : font; + + int pointSize = activeFont.getPointSize(); + int numChars = contents.length(); + + if (activeFont.isItalic() || + activeFont.getBoldWeight() > 400) // magic value for normal bold + { + numChars += 2; + } + + int points = numChars * pointSize; + maxWidth = Math.max(maxWidth, points * 256); + } + } + cir.setWidth((int) (maxWidth / defaultFont.getPointSize())); + } + + /** + * Imports a sheet from a different workbook + * + * @param s the sheet to import + */ + void importSheet(Sheet s) + { + // Copy the settings + settings = new SheetSettings(s.getSettings(), this); + + SheetCopier si = new SheetCopier(s, this); + si.setColumnFormats(columnFormats); + si.setFormatRecords(formatRecords); + si.setHyperlinks(hyperlinks); + si.setMergedCells(mergedCells); + si.setRowBreaks(rowBreaks); + si.setColumnBreaks(columnBreaks); + si.setSheetWriter(sheetWriter); + si.setDrawings(drawings); + si.setImages(images); + + si.importSheet(); + + dataValidation = si.getDataValidation(); + comboBox = si.getComboBox(); + plsRecord = si.getPLSRecord(); + chartOnly = si.isChartOnly(); + buttonPropertySet = si.getButtonPropertySet(); + numRows = si.getRows(); + maxRowOutlineLevel = si.getMaxRowOutlineLevel(); + maxColumnOutlineLevel = si.getMaxColumnOutlineLevel(); + } +} Index: 3rdParty_sources/jexcelapi/jxl/write/biff/WritableWorkbookImpl.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/WritableWorkbookImpl.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/WritableWorkbookImpl.java 17 Aug 2012 14:51:11 -0000 1.1 @@ -0,0 +1,1869 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; + +import common.Assert; +import common.Logger; + +import jxl.Range; +import jxl.Sheet; +import jxl.Workbook; +import jxl.WorkbookSettings; +import jxl.biff.BuiltInName; +import jxl.biff.CellReferenceHelper; +import jxl.biff.CountryCode; +import jxl.biff.Fonts; +import jxl.biff.FormattingRecords; +import jxl.biff.IndexMapping; +import jxl.biff.IntegerHelper; +import jxl.biff.RangeImpl; +import jxl.biff.WorkbookMethods; +import jxl.biff.drawing.Drawing; +import jxl.biff.drawing.DrawingGroup; +import jxl.biff.drawing.DrawingGroupObject; +import jxl.biff.drawing.Origin; +import jxl.biff.formula.ExternalSheet; +import jxl.format.Colour; +import jxl.format.RGB; +import jxl.read.biff.WorkbookParser; +import jxl.write.WritableCell; +import jxl.write.WritableSheet; +import jxl.write.WritableWorkbook; + + +/** + * A writable workbook + */ +public class WritableWorkbookImpl extends WritableWorkbook + implements ExternalSheet, WorkbookMethods +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(WritableWorkbookImpl.class); + /** + * The list of formats available within this workbook + */ + private FormattingRecords formatRecords; + /** + * The output file to write the workbook to + */ + private File outputFile; + /** + * The list of sheets within this workbook + */ + private ArrayList sheets; + /** + * The list of fonts available within this workbook + */ + private Fonts fonts; + /** + * The list of external sheets, used by cell references in formulas + */ + private ExternalSheetRecord externSheet; + + /** + * The supbook records + */ + private ArrayList supbooks; + + /** + * The name records + */ + private ArrayList names; + + /** + * A lookup hash map of the name records + */ + private HashMap nameRecords; + + /** + * The shared strings used by this workbook + */ + private SharedStrings sharedStrings; + + /** + * Indicates whether or not the output stream should be closed. This + * depends on whether this Workbook was created with an output stream, + * or a flat file (flat file closes the stream + */ + private boolean closeStream; + + /** + * The workbook protection flag + */ + private boolean wbProtected; + + /** + * The settings for the workbook + */ + private WorkbookSettings settings; + + /** + * The list of cells for the entire workbook which need to be updated + * following a row/column insert or remove + */ + private ArrayList rcirCells; + + /** + * The drawing group + */ + private DrawingGroup drawingGroup; + + /** + * The common workbook styles + */ + private Styles styles; + + /** + * Contains macros flag + */ + private boolean containsMacros; + + /** + * The buttons property set + */ + private ButtonPropertySetRecord buttonPropertySet; + + /** + * The country record, initialised when copying a workbook + */ + private CountryRecord countryRecord; + + // synchronizer object for static unitiatialization + private static Object SYNCHRONIZER = new Object(); + /** + * The names of any add in functions + */ + private String[] addInFunctionNames; + + /** + * Constructor. Writes the workbook direct to the existing output stream + * + * @exception IOException + * @param os the output stream + * @param cs TRUE if the workbook should close the output stream, FALSE + * @param ws the configuration for this workbook + * otherwise + */ + public WritableWorkbookImpl(OutputStream os, boolean cs, WorkbookSettings ws) + throws IOException + { + super(); + outputFile = new File(os, ws, null); + sheets = new ArrayList(); + sharedStrings = new SharedStrings(); + nameRecords = new HashMap(); + closeStream = cs; + wbProtected = false; + containsMacros = false; + settings = ws; + rcirCells = new ArrayList(); + styles = new Styles(); + + // Reset the statically declared styles. These are no longer needed + // because the Styles class will intercept all calls within + // CellValue.setCellDetails and if it detects a standard format, then it + // will return a clone. In short, the static cell values will + // never get initialized anyway. Still, just to be extra sure... + synchronized(SYNCHRONIZER) + { + WritableWorkbook.ARIAL_10_PT.uninitialize(); + WritableWorkbook.HYPERLINK_FONT.uninitialize(); + WritableWorkbook.NORMAL_STYLE.uninitialize(); + WritableWorkbook.HYPERLINK_STYLE.uninitialize(); + WritableWorkbook.HIDDEN_STYLE.uninitialize(); + DateRecord.defaultDateFormat.uninitialize(); + } + + WritableFonts wf = new WritableFonts(this); + fonts = wf; + + WritableFormattingRecords wfr = new WritableFormattingRecords(fonts, + styles); + formatRecords = wfr; + } + + /** + * A pseudo copy constructor. Takes the handles to the font and formatting + * records + * + * @exception IOException + * @param w the workbook to copy + * @param os the output stream to write the data to + * @param cs TRUE if the workbook should close the output stream, FALSE + * @param ws the configuration for this workbook + */ + public WritableWorkbookImpl(OutputStream os, + Workbook w, + boolean cs, + WorkbookSettings ws) throws IOException + { + super(); + WorkbookParser wp = (WorkbookParser) w; + + // Reset the statically declared styles. These are no longer needed + // because the Styles class will intercept all calls within + // CellValue.setCellDetails and if it detects a standard format, then it + // will return a clone. In short, the static cell values will + // never get initialized anyway. Still, just to be extra sure... + synchronized(SYNCHRONIZER) + { + WritableWorkbook.ARIAL_10_PT.uninitialize(); + WritableWorkbook.HYPERLINK_FONT.uninitialize(); + WritableWorkbook.NORMAL_STYLE.uninitialize(); + WritableWorkbook.HYPERLINK_STYLE.uninitialize(); + WritableWorkbook.HIDDEN_STYLE.uninitialize(); + DateRecord.defaultDateFormat.uninitialize(); + } + + closeStream = cs; + sheets = new ArrayList(); + sharedStrings = new SharedStrings(); + nameRecords = new HashMap(); + fonts = wp.getFonts(); + formatRecords = wp.getFormattingRecords(); + wbProtected = false; + settings = ws; + rcirCells = new ArrayList(); + styles = new Styles(); + outputFile = new File(os, ws, wp.getCompoundFile()); + + containsMacros = false; + if (!ws.getPropertySetsDisabled()) + { + containsMacros = wp.containsMacros(); + } + + // Copy the country settings + if (wp.getCountryRecord() != null) + { + countryRecord = new CountryRecord(wp.getCountryRecord()); + } + + // Copy any add in functions + addInFunctionNames = wp.getAddInFunctionNames(); + + // Copy any external sheets + if (wp.getExternalSheetRecord() != null) + { + externSheet = new ExternalSheetRecord(wp.getExternalSheetRecord()); + + // Get the associated supbooks + jxl.read.biff.SupbookRecord[] readsr = wp.getSupbookRecords(); + supbooks = new ArrayList(readsr.length); + + for (int i = 0; i < readsr.length; i++) + { + jxl.read.biff.SupbookRecord readSupbook = readsr[i]; + if (readSupbook.getType() == readSupbook.INTERNAL || + readSupbook.getType() == readSupbook.EXTERNAL) + { + supbooks.add(new SupbookRecord(readSupbook, settings)); + } + else + { + if (readSupbook.getType() != readSupbook.ADDIN) + { + logger.warn("unsupported supbook type - ignoring"); + } + } + } + } + + // Copy any drawings. These must be present before we try and copy + // the images from the read workbook + if (wp.getDrawingGroup() != null) + { + drawingGroup = new DrawingGroup(wp.getDrawingGroup()); + } + + // Copy the property set references + if (containsMacros && wp.getButtonPropertySet() != null) + { + buttonPropertySet = new ButtonPropertySetRecord + (wp.getButtonPropertySet()); + } + + // Copy any names + if (!settings.getNamesDisabled()) + { + jxl.read.biff.NameRecord[] na = wp.getNameRecords(); + names = new ArrayList(na.length); + + for (int i = 0; i < na.length; i++) + { + if (na[i].isBiff8()) + { + NameRecord n = new NameRecord(na[i], i); + names.add(n); + String name = n.getName(); + nameRecords.put(name, n); + } + else + { + logger.warn("Cannot copy Biff7 name records - ignoring"); + } + } + } + + copyWorkbook(w); + + // The copy process may have caused some critical fields in the + // read drawing group to change. Make sure these updates are reflected + // in the writable drawing group + if (drawingGroup != null) + { + drawingGroup.updateData(wp.getDrawingGroup()); + } + } + + /** + * Gets the sheets within this workbook. Use of this method for + * large worksheets can cause performance problems. + * + * @return an array of the individual sheets + */ + public WritableSheet[] getSheets() + { + WritableSheet[] sheetArray = new WritableSheet[getNumberOfSheets()]; + + for (int i = 0 ; i < getNumberOfSheets() ; i++) + { + sheetArray[i] = getSheet(i); + } + return sheetArray; + } + + /** + * Gets the sheet names + * + * @return an array of strings containing the sheet names + */ + public String[] getSheetNames() + { + String[] sheetNames = new String[getNumberOfSheets()]; + + for (int i = 0 ; i < sheetNames.length ; i++) + { + sheetNames[i] = getSheet(i).getName(); + } + + return sheetNames; + } + + /** + * Interface method from WorkbookMethods - gets the specified + * sheet within this workbook + * + * @param index the zero based index of the required sheet + * @return The sheet specified by the index + */ + public Sheet getReadSheet(int index) + { + return getSheet(index); + } + + /** + * Gets the specified sheet within this workbook + * + * @param index the zero based index of the reQuired sheet + * @return The sheet specified by the index + */ + public WritableSheet getSheet(int index) + { + return (WritableSheet) sheets.get(index); + } + + /** + * Gets the sheet with the specified name from within this workbook + * + * @param name the sheet name + * @return The sheet with the specified name, or null if it is not found + */ + public WritableSheet getSheet(String name) + { + // Iterate through the boundsheet records + boolean found = false; + Iterator i = sheets.iterator(); + WritableSheet s = null; + + while (i.hasNext() && !found) + { + s = (WritableSheet) i.next(); + + if (s.getName().equals(name)) + { + found = true; + } + } + + return found ? s : null; + } + + /** + * Returns the number of sheets in this workbook + * + * @return the number of sheets in this workbook + */ + public int getNumberOfSheets() + { + return sheets.size(); + } + + /** + * Closes this workbook, and frees makes any memory allocated available + * for garbage collection + * + * @exception IOException + * @exception JxlWriteException + */ + public void close() throws IOException, JxlWriteException + { + outputFile.close(closeStream); + } + + /** + * Sets a new output file. This allows the smae workbook to be + * written to various different output files without having to + * read in any templates again + * + * @param fileName the file name + * @exception IOException + */ + public void setOutputFile(java.io.File fileName) throws IOException + { + FileOutputStream fos = new FileOutputStream(fileName); + outputFile.setOutputFile(fos); + } + + + /** + * The internal method implementation for creating new sheets + * + * @param name + * @param index + * @param handleRefs flag indicating whether or not to handle external + * sheet references + * @return + */ + private WritableSheet createSheet(String name, int index, + boolean handleRefs) + { + WritableSheet w = new WritableSheetImpl(name, + outputFile, + formatRecords, + sharedStrings, + settings, + this); + + int pos = index; + + if (index <= 0) + { + pos = 0; + sheets.add(0, w); + } + else if (index > sheets.size()) + { + pos = sheets.size(); + sheets.add(w); + } + else + { + sheets.add(index, w); + } + + if (handleRefs && externSheet != null) + { + externSheet.sheetInserted(pos); + } + + if (supbooks != null && supbooks.size() > 0) + { + SupbookRecord supbook = (SupbookRecord) supbooks.get(0); + if (supbook.getType() == SupbookRecord.INTERNAL) + { + supbook.adjustInternal(sheets.size()); + } + } + + return w; + } + + /** + * Creates a new sheet within the workbook, at the specified position. + * The new sheet is inserted at the specified position, or prepended/appended + * to the list of sheets if the index specified is somehow inappropriate + * + * @param name the name of the new sheet + * @param index the index at which to add the sheet + * @return the created sheet + */ + public WritableSheet createSheet(String name, int index) + { + return createSheet(name, index, true); + } + + /** + * Removes a sheet from this workbook, the other sheets indices being + * altered accordingly. If the sheet referenced by the index + * does not exist, then no action is taken. + * + * @param index the index of the sheet to remove + */ + public void removeSheet(int index) + { + int pos = index; + if (index <= 0) + { + pos = 0; + sheets.remove(0); + } + else if (index >= sheets.size()) + { + pos = sheets.size() - 1; + sheets.remove(sheets.size() - 1); + } + else + { + sheets.remove(index); + } + + if (externSheet != null) + { + externSheet.sheetRemoved(pos); + } + + if (supbooks != null && supbooks.size() > 0) + { + SupbookRecord supbook = (SupbookRecord) supbooks.get(0); + if (supbook.getType() == SupbookRecord.INTERNAL) + { + supbook.adjustInternal(sheets.size()); + } + } + + if (names != null && names.size() > 0) + { + for (int i=0; i< names.size();i++) + { + NameRecord n = (NameRecord) names.get(i); + int oldRef = n.getSheetRef(); + if(oldRef == (pos+1)) + { + n.setSheetRef(0); // make a global name reference + } + else if (oldRef > (pos+1)) + { + if(oldRef < 1) + { + oldRef = 1; + } + n.setSheetRef(oldRef-1); // move one sheet + } + } + } + } + + /** + * Moves the specified sheet within this workbook to another index + * position. + * + * @param fromIndex the zero based index of the reQuired sheet + * @param toIndex the zero based index of the reQuired sheet + * @return the sheet that has been moved + */ + public WritableSheet moveSheet(int fromIndex, int toIndex) + { + // Handle dodgy index + fromIndex = Math.max(fromIndex, 0); + fromIndex = Math.min(fromIndex, sheets.size() - 1); + toIndex = Math.max(toIndex, 0); + toIndex = Math.min(toIndex, sheets.size() - 1); + + WritableSheet sheet= (WritableSheet)sheets.remove(fromIndex); + sheets.add(toIndex, sheet); + + return sheet; + } + + /** + * Writes out this sheet to the output file. First it writes out + * the standard workbook information required by excel, before calling + * the write method on each sheet individually + * + * @exception IOException + */ + public void write() throws IOException + { + // Perform some preliminary sheet check before we start writing out + // the workbook + WritableSheetImpl wsi = null; + for (int i = 0; i < getNumberOfSheets(); i++) + { + wsi = (WritableSheetImpl) getSheet(i); + + // Check the merged records. This has to be done before the + // globals are written out because some more XF formats might be created + wsi.checkMergedBorders(); + + // Check to see if there are any predefined names + Range range = wsi.getSettings().getPrintArea(); + if (range != null) + { + addNameArea(BuiltInName.PRINT_AREA, + wsi, + range.getTopLeft().getColumn(), + range.getTopLeft().getRow(), + range.getBottomRight().getColumn(), + range.getBottomRight().getRow(), + false); + } + + // Check to see if print titles by row were set + Range rangeR = wsi.getSettings().getPrintTitlesRow(); + Range rangeC = wsi.getSettings().getPrintTitlesCol(); + if (rangeR != null && rangeC != null) + { + addNameArea(BuiltInName.PRINT_TITLES, + wsi, + rangeR.getTopLeft().getColumn(), + rangeR.getTopLeft().getRow(), + rangeR.getBottomRight().getColumn(), + rangeR.getBottomRight().getRow(), + rangeC.getTopLeft().getColumn(), + rangeC.getTopLeft().getRow(), + rangeC.getBottomRight().getColumn(), + rangeC.getBottomRight().getRow(), + false); + } + // Check to see if print titles by row were set + else if (rangeR != null) + { + addNameArea(BuiltInName.PRINT_TITLES, + wsi, + rangeR.getTopLeft().getColumn(), + rangeR.getTopLeft().getRow(), + rangeR.getBottomRight().getColumn(), + rangeR.getBottomRight().getRow(), + false); + } + // Check to see if print titles by column were set + else if (rangeC != null) + { + addNameArea(BuiltInName.PRINT_TITLES, + wsi, + rangeC.getTopLeft().getColumn(), + rangeC.getTopLeft().getRow(), + rangeC.getBottomRight().getColumn(), + rangeC.getBottomRight().getRow(), + false); + } + } + + // Rationalize all the XF and number formats + if (!settings.getRationalizationDisabled()) + { + rationalize(); + } + + // Write the workbook globals + BOFRecord bof = new BOFRecord(BOFRecord.workbookGlobals); + outputFile.write(bof); + + InterfaceHeaderRecord ihr = new InterfaceHeaderRecord(); + outputFile.write(ihr); + + MMSRecord mms = new MMSRecord(0,0); + outputFile.write(mms); + + InterfaceEndRecord ier = new InterfaceEndRecord(); + outputFile.write(ier); + + WriteAccessRecord wr = new WriteAccessRecord(); + outputFile.write(wr); + + CodepageRecord cp = new CodepageRecord(); + outputFile.write(cp); + + DSFRecord dsf = new DSFRecord(); + outputFile.write(dsf); + + TabIdRecord tabid = new TabIdRecord(getNumberOfSheets()); + outputFile.write(tabid); + + if (containsMacros) + { + ObjProjRecord objproj = new ObjProjRecord(); + outputFile.write(objproj); + } + + if (buttonPropertySet != null) + { + outputFile.write(buttonPropertySet); + } + + FunctionGroupCountRecord fgcr = new FunctionGroupCountRecord(); + outputFile.write(fgcr); + + // do not support password protected workbooks + WindowProtectRecord wpr = new WindowProtectRecord(false); + outputFile.write(wpr); + + ProtectRecord pr = new ProtectRecord(wbProtected); + outputFile.write(pr); + + PasswordRecord pw = new PasswordRecord(null); + outputFile.write(pw); + + Prot4RevRecord p4r = new Prot4RevRecord(false); + outputFile.write(p4r); + + Prot4RevPassRecord p4rp = new Prot4RevPassRecord(); + outputFile.write(p4rp); + + // If no sheet is identified as being selected, then select + // the first one + boolean sheetSelected = false; + WritableSheetImpl wsheet = null; + int selectedSheetIndex = 0; + for (int i = 0 ; i < getNumberOfSheets() && !sheetSelected ; i++) + { + wsheet = (WritableSheetImpl) getSheet(i); + if (wsheet.getSettings().isSelected()) + { + sheetSelected = true; + selectedSheetIndex = i; + } + } + + if (!sheetSelected) + { + wsheet = (WritableSheetImpl) getSheet(0); + wsheet.getSettings().setSelected(true); + selectedSheetIndex = 0; + } + + Window1Record w1r = new Window1Record(selectedSheetIndex); + outputFile.write(w1r); + + BackupRecord bkr = new BackupRecord(false); + outputFile.write(bkr); + + HideobjRecord ho = new HideobjRecord(false); + outputFile.write(ho); + + NineteenFourRecord nf = new NineteenFourRecord(false); + outputFile.write(nf); + + PrecisionRecord pc = new PrecisionRecord(false); + outputFile.write(pc); + + RefreshAllRecord rar = new RefreshAllRecord(false); + outputFile.write(rar); + + BookboolRecord bb = new BookboolRecord(true); + outputFile.write(bb); + + // Write out all the fonts used + fonts.write(outputFile); + + // Write out the cell formats used within this workbook + formatRecords.write(outputFile); + + // Write out the palette, if it exists + if (formatRecords.getPalette() != null) + { + outputFile.write(formatRecords.getPalette()); + } + + // Write out the uses elfs record + UsesElfsRecord uer = new UsesElfsRecord(); + outputFile.write(uer); + + // Write out the boundsheet records. Keep a handle to each one's + // position so we can write in the stream offset later + int[] boundsheetPos = new int[getNumberOfSheets()]; + Sheet sheet = null; + + for (int i = 0; i < getNumberOfSheets(); i++) + { + boundsheetPos[i] = outputFile.getPos(); + sheet = getSheet(i); + BoundsheetRecord br = new BoundsheetRecord(sheet.getName()); + if (sheet.getSettings().isHidden()) + { + br.setHidden(); + } + + if ( ( (WritableSheetImpl) sheets.get(i)).isChartOnly()) + { + br.setChartOnly(); + } + + outputFile.write(br); + } + + if (countryRecord == null) + { + CountryCode lang = + CountryCode.getCountryCode(settings.getExcelDisplayLanguage()); + if (lang == CountryCode.UNKNOWN) + { + logger.warn("Unknown country code " + + settings.getExcelDisplayLanguage() + + " using " + CountryCode.USA.getCode()); + lang = CountryCode.USA; + } + CountryCode region = + CountryCode.getCountryCode(settings.getExcelRegionalSettings()); + countryRecord = new CountryRecord(lang, region); + if (region == CountryCode.UNKNOWN) + { + logger.warn("Unknown country code " + + settings.getExcelDisplayLanguage() + + " using " + CountryCode.UK.getCode()); + region = CountryCode.UK; + } + } + + outputFile.write(countryRecord); + + // Write out the names of any add in functions + if (addInFunctionNames != null && addInFunctionNames.length > 0) + { + // Write out the supbook record + SupbookRecord supbook = new SupbookRecord(); + outputFile.write(supbook); + + for (int i = 0 ; i < addInFunctionNames.length; i++) + { + ExternalNameRecord enr = new ExternalNameRecord(addInFunctionNames[i]); + outputFile.write(enr); + } + } + + // Write out the external sheet record, if it exists + if (externSheet != null) + { + //Write out all the supbook records + for (int i = 0; i < supbooks.size() ; i++) + { + SupbookRecord supbook = (SupbookRecord) supbooks.get(i); + outputFile.write(supbook); + } + outputFile.write(externSheet); + } + + // Write out the names, if any exists + if (names != null) + { + for (int i = 0 ; i < names.size() ; i++) + { + NameRecord n = (NameRecord) names.get(i); + outputFile.write(n); + } + } + + // Write out the mso drawing group, if it exists + if (drawingGroup != null) + { + drawingGroup.write(outputFile); + } + + sharedStrings.write(outputFile); + + EOFRecord eof = new EOFRecord(); + outputFile.write(eof); + + + // Write out the sheets + for (int i = 0; i < getNumberOfSheets(); i++) + { + // first go back and modify the offset we wrote out for the + // boundsheet record + outputFile.setData + (IntegerHelper.getFourBytes(outputFile.getPos()), + boundsheetPos[i] + 4); + + wsheet = (WritableSheetImpl) getSheet(i); + wsheet.write(); + } + } + + /** + * Produces a writable copy of the workbook passed in by + * creating copies of each sheet in the specified workbook and adding + * them to its own record + * + * @param w the workbook to copy + */ + private void copyWorkbook(Workbook w) + { + int numSheets = w.getNumberOfSheets(); + wbProtected = w.isProtected(); + Sheet s = null; + WritableSheetImpl ws = null; + for (int i = 0 ; i < numSheets; i++) + { + s = w.getSheet(i); + ws = (WritableSheetImpl) createSheet(s.getName(),i, false); + ws.copy(s); + } + } + + /** + * Copies the specified sheet and places it at the index + * specified by the parameter + * + * @param s the index of the sheet to copy + * @param name the name of the new sheet + * @param index the position of the new sheet + */ + public void copySheet(int s, String name, int index) + { + WritableSheet sheet = getSheet(s); + WritableSheetImpl ws = (WritableSheetImpl) createSheet(name, index); + ws.copy(sheet); + } + + /** + * Copies the specified sheet and places it at the index + * specified by the parameter + * + * @param s the name of the sheet to copy + * @param name the name of the new sheet + * @param index the position of the new sheet + */ + public void copySheet(String s, String name, int index) + { + WritableSheet sheet = getSheet(s); + WritableSheetImpl ws = (WritableSheetImpl) createSheet(name, index); + ws.copy(sheet); + } + + /** + * Indicates whether or not this workbook is protected + * + * @param prot protected flag + */ + public void setProtected(boolean prot) + { + wbProtected = prot; + } + + /** + * Rationalizes the cell formats, and then passes the resultant XF index + * mappings to each sheet in turn + */ + private void rationalize() + { + IndexMapping fontMapping = formatRecords.rationalizeFonts(); + IndexMapping formatMapping = formatRecords.rationalizeDisplayFormats(); + IndexMapping xfMapping = formatRecords.rationalize(fontMapping, + formatMapping); + + WritableSheetImpl wsi = null; + for (int i = 0; i < sheets.size(); i++) + { + wsi = (WritableSheetImpl) sheets.get(i); + wsi.rationalize(xfMapping, fontMapping, formatMapping); + } + } + + /** + * Gets the internal sheet index for a sheet name + * + * @param name the sheet name + * @return the internal sheet index + */ + private int getInternalSheetIndex(String name) + { + int index = -1; + String[] names = getSheetNames(); + for (int i = 0 ; i < names.length; i++) + { + if (name.equals(names[i])) + { + index = i; + break; + } + } + + return index; + } + + /** + * Gets the name of the external sheet specified by the index + * + * @param index the external sheet index + * @return the name of the external sheet + */ + public String getExternalSheetName(int index) + { + int supbookIndex = externSheet.getSupbookIndex(index); + SupbookRecord sr = (SupbookRecord) supbooks.get(supbookIndex); + + int firstTab = externSheet.getFirstTabIndex(index); + + if (sr.getType() == SupbookRecord.INTERNAL) + { + // It's an internal reference - get the name from the sheets list + WritableSheet ws = getSheet(firstTab); + + return ws.getName(); + } + else if (sr.getType() == SupbookRecord.EXTERNAL) + { + String name = sr.getFileName() + sr.getSheetName(firstTab); + return name; + } + + // An unknown supbook - return unkown + return "[UNKNOWN]"; + } + + /** + * Gets the name of the last external sheet specified by the index + * + * @param index the external sheet index + * @return the name of the external sheet + */ + public String getLastExternalSheetName(int index) + { + int supbookIndex = externSheet.getSupbookIndex(index); + SupbookRecord sr = (SupbookRecord) supbooks.get(supbookIndex); + + int lastTab = externSheet.getLastTabIndex(index); + + if (sr.getType() == SupbookRecord.INTERNAL) + { + // It's an internal reference - get the name from the sheets list + WritableSheet ws = getSheet(lastTab); + + return ws.getName(); + } + else if (sr.getType() == SupbookRecord.EXTERNAL) + { + Assert.verify(false); + } + + // An unknown supbook - return unkown + return "[UNKNOWN]"; + } + + /** + * Parsing of formulas is only supported for a subset of the available + * biff version, so we need to test to see if this version is acceptable + * + * @return the BOF record, which + */ + public jxl.read.biff.BOFRecord getWorkbookBof() + { + return null; + } + + + /** + * Gets the index of the external sheet for the name + * + * @param sheetName + * @return the sheet index of the external sheet index + */ + public int getExternalSheetIndex(int index) + { + if (externSheet == null) + { + return index; + } + + Assert.verify(externSheet != null); + + int firstTab = externSheet.getFirstTabIndex(index); + + return firstTab; + } + + /** + * Gets the index of the external sheet for the name + * + * @param sheetName + * @return the sheet index of the external sheet index + */ + public int getLastExternalSheetIndex(int index) + { + if (externSheet == null) + { + return index; + } + + Assert.verify(externSheet != null); + + int lastTab = externSheet.getLastTabIndex(index); + + return lastTab; + } + + /** + * Gets the external sheet index for the sheet name + * + * @param sheetName + * @return the sheet index or -1 if the sheet could not be found + */ + public int getExternalSheetIndex(String sheetName) + { + if (externSheet == null) + { + externSheet = new ExternalSheetRecord(); + supbooks = new ArrayList(); + supbooks.add(new SupbookRecord(getNumberOfSheets(), settings)); + } + + // Iterate through the sheets records + boolean found = false; + Iterator i = sheets.iterator(); + int sheetpos = 0; + WritableSheetImpl s = null; + + while (i.hasNext() && !found) + { + s = (WritableSheetImpl) i.next(); + + if (s.getName().equals(sheetName)) + { + found = true; + } + else + { + sheetpos++; + } + } + + if (found) + { + // Check that the supbook record at position zero is internal and + // contains all the sheets + SupbookRecord supbook = (SupbookRecord) supbooks.get(0); + if (supbook.getType() != SupbookRecord.INTERNAL || + supbook.getNumberOfSheets() != getNumberOfSheets()) + { + logger.warn("Cannot find sheet " + sheetName + " in supbook record"); + } + + return externSheet.getIndex(0, sheetpos); + } + + // Check for square brackets + int closeSquareBracketsIndex = sheetName.lastIndexOf(']'); + int openSquareBracketsIndex = sheetName.lastIndexOf('['); + + if (closeSquareBracketsIndex == -1 || + openSquareBracketsIndex == -1) + { + return -1; + } + + String worksheetName = sheetName.substring(closeSquareBracketsIndex+1); + String workbookName = sheetName.substring(openSquareBracketsIndex+1, + closeSquareBracketsIndex); + String path = sheetName.substring(0, openSquareBracketsIndex); + String fileName = path + workbookName; + + boolean supbookFound = false; + SupbookRecord externalSupbook = null; + int supbookIndex = -1; + for (int ind = 0; ind < supbooks.size() && !supbookFound ; ind++) + { + externalSupbook = (SupbookRecord) supbooks.get(ind); + if (externalSupbook.getType() == SupbookRecord.EXTERNAL && + externalSupbook.getFileName().equals(fileName)) + { + supbookFound = true; + supbookIndex = ind; + } + } + + if (!supbookFound) + { + externalSupbook = new SupbookRecord(fileName, settings); + supbookIndex = supbooks.size(); + supbooks.add(externalSupbook); + } + + int sheetIndex = externalSupbook.getSheetIndex(worksheetName); + + return externSheet.getIndex(supbookIndex, sheetIndex); + } + + /** + * Gets the last external sheet index for the sheet name + * @param sheetName + * @return the sheet index or -1 if the sheet could not be found + */ + public int getLastExternalSheetIndex(String sheetName) + { + if (externSheet == null) + { + externSheet = new ExternalSheetRecord(); + supbooks = new ArrayList(); + supbooks.add(new SupbookRecord(getNumberOfSheets(), settings)); + } + + // Iterate through the sheets records + boolean found = false; + Iterator i = sheets.iterator(); + int sheetpos = 0; + WritableSheetImpl s = null; + + while (i.hasNext() && !found) + { + s = (WritableSheetImpl) i.next(); + + if (s.getName().equals(sheetName)) + { + found = true; + } + else + { + sheetpos++; + } + } + + if (!found) + { + return -1; + } + + // Check that the supbook record at position zero is internal and contains + // all the sheets + SupbookRecord supbook = (SupbookRecord) supbooks.get(0); + Assert.verify(supbook.getType() == SupbookRecord.INTERNAL && + supbook.getNumberOfSheets() == getNumberOfSheets()); + + return externSheet.getIndex(0, sheetpos); + } + + /** + * Sets the RGB value for the specified colour for this workbook + * + * @param c the colour whose RGB value is to be overwritten + * @param r the red portion to set (0-255) + * @param g the green portion to set (0-255) + * @param b the blue portion to set (0-255) + */ + public void setColourRGB(Colour c, int r, int g, int b) + { + formatRecords.setColourRGB(c,r,g,b); + } + + /** + * Accessor for the RGB value for the specified colour + * + * @return the RGB for the specified colour + */ + public RGB getColourRGB(Colour c) + { + return formatRecords.getColourRGB(c); + } + + /** + * Gets the name at the specified index + * + * @param index the index into the name table + * @return the name of the cell + */ + public String getName(int index) + { + Assert.verify(index >= 0 && index < names.size()); + NameRecord n = (NameRecord) names.get(index); + return n.getName(); + } + + /** + * Gets the index of the name record for the name + * + * @param name + * @return the index in the name table + */ + public int getNameIndex(String name) + { + NameRecord nr = (NameRecord) nameRecords.get(name); + return nr != null ? nr.getIndex() : -1; + } + + /** + * Adds a cell to workbook wide range of cells which need adjustment + * following a row/column insert or remove + * + * @param f the cell to add to the list + */ + void addRCIRCell(CellValue cv) + { + rcirCells.add(cv); + } + + /** + * Called when a column is inserted on the specified sheet. Notifies all + * RCIR cells of this change + * + * @param s the sheet on which the column was inserted + * @param col the column number which was inserted + */ + void columnInserted(WritableSheetImpl s, int col) + { + int externalSheetIndex = getExternalSheetIndex(s.getName()); + for (Iterator i = rcirCells.iterator() ; i.hasNext() ;) + { + CellValue cv = (CellValue) i.next(); + cv.columnInserted(s, externalSheetIndex, col); + } + + // Adjust any named cells + if (names != null) + { + for (Iterator i = names.iterator(); i.hasNext() ;) + { + NameRecord nameRecord = (NameRecord) i.next(); + nameRecord.columnInserted(externalSheetIndex, col); + } + } + } + + /** + * Called when a column is removed on the specified sheet. Notifies all + * RCIR cells of this change + * + * @param s the sheet on which the column was removed + * @param col the column number which was removed + */ + void columnRemoved(WritableSheetImpl s, int col) + { + int externalSheetIndex = getExternalSheetIndex(s.getName()); + for (Iterator i = rcirCells.iterator() ; i.hasNext() ;) + { + CellValue cv = (CellValue) i.next(); + cv.columnRemoved(s, externalSheetIndex, col); + } + + // Adjust any named cells + ArrayList removedNames = new ArrayList(); + if (names != null) + { + for (Iterator i = names.iterator(); i.hasNext() ;) + { + NameRecord nameRecord = (NameRecord) i.next(); + boolean removeName = nameRecord.columnRemoved(externalSheetIndex, + col); + + if (removeName) + { + removedNames.add(nameRecord); + } + } + + // Remove any names which have been deleted + for (Iterator i = removedNames.iterator(); i.hasNext() ;) + { + NameRecord nameRecord = (NameRecord) i.next(); + boolean removed = names.remove(nameRecord); + Assert.verify(removed, "Could not remove name " + + nameRecord.getName()); + } + } + } + + /** + * Called when a row is inserted on the specified sheet. Notifies all + * RCIR cells of this change + * + * @param s the sheet on which the row was inserted + * @param row the row number which was inserted + */ + void rowInserted(WritableSheetImpl s, int row) + { + int externalSheetIndex = getExternalSheetIndex(s.getName()); + + // Adjust the row infos + for (Iterator i = rcirCells.iterator() ; i.hasNext() ;) + { + CellValue cv = (CellValue) i.next(); + cv.rowInserted(s, externalSheetIndex, row); + } + + // Adjust any named cells + if (names != null) + { + for (Iterator i = names.iterator(); i.hasNext() ;) + { + NameRecord nameRecord = (NameRecord) i.next(); + nameRecord.rowInserted(externalSheetIndex, row); + } + } + } + + /** + * Called when a row is removed on the specified sheet. Notifies all + * RCIR cells of this change + * + * @param s the sheet on which the row was removed + * @param row the row number which was removed + */ + void rowRemoved(WritableSheetImpl s, int row) + { + int externalSheetIndex = getExternalSheetIndex(s.getName()); + for (Iterator i = rcirCells.iterator() ; i.hasNext() ;) + { + CellValue cv = (CellValue) i.next(); + cv.rowRemoved(s, externalSheetIndex, row); + } + + // Adjust any named cells + ArrayList removedNames = new ArrayList(); + if (names != null) + { + for (Iterator i = names.iterator(); i.hasNext() ;) + { + NameRecord nameRecord = (NameRecord) i.next(); + boolean removeName = nameRecord.rowRemoved(externalSheetIndex, row); + + if (removeName) + { + removedNames.add(nameRecord); + } + } + + // Remove any names which have been deleted + for (Iterator i = removedNames.iterator(); i.hasNext() ;) + { + NameRecord nameRecord = (NameRecord) i.next(); + boolean removed = names.remove(nameRecord); + Assert.verify(removed, "Could not remove name " + + nameRecord.getName()); + } + } + } + + /** + * Gets the named cell from this workbook. If the name refers to a + * range of cells, then the cell on the top left is returned. If + * the name cannot be found, null is returned + * + * @param the name of the cell/range to search for + * @return the cell in the top left of the range if found, NULL + * otherwise + */ + public WritableCell findCellByName(String name) + { + NameRecord nr = (NameRecord) nameRecords.get(name); + + if (nr == null) + { + return null; + } + + NameRecord.NameRange[] ranges = nr.getRanges(); + + // Go and retrieve the first cell in the first range + int sheetIndex = getExternalSheetIndex(ranges[0].getExternalSheet()); + WritableSheet s = getSheet(sheetIndex); + WritableCell cell = s.getWritableCell(ranges[0].getFirstColumn(), + ranges[0].getFirstRow()); + + return cell; + } + + /** + * Gets the named range from this workbook. The Range object returns + * contains all the cells from the top left to the bottom right + * of the range. + * If the named range comprises an adjacent range, + * the Range[] will contain one object; for non-adjacent + * ranges, it is necessary to return an array of length greater than + * one. + * If the named range contains a single cell, the top left and + * bottom right cell will be the same cell + * + * @param the name of the cell/range to search for + * @return the range of cells + */ + public Range[] findByName(String name) + { + NameRecord nr = (NameRecord) nameRecords.get(name); + + if (nr == null) + { + return null; + } + + NameRecord.NameRange[] ranges = nr.getRanges(); + + Range[] cellRanges = new Range[ranges.length]; + + for (int i = 0; i < ranges.length ; i++) + { + cellRanges[i] = new RangeImpl + (this, + getExternalSheetIndex(ranges[i].getExternalSheet()), + ranges[i].getFirstColumn(), + ranges[i].getFirstRow(), + getLastExternalSheetIndex(ranges[i].getExternalSheet()), + ranges[i].getLastColumn(), + ranges[i].getLastRow()); + } + + return cellRanges; + } + + /** + * Adds a drawing to this workbook + * + * @param d the drawing to add + */ + void addDrawing(DrawingGroupObject d) + { + if (drawingGroup == null) + { + drawingGroup = new DrawingGroup(Origin.WRITE); + } + + drawingGroup.add(d); + } + + /** + * Removes a drawing from this workbook + * + * @param d the drawing to remove + */ + void removeDrawing(Drawing d) + { + Assert.verify(drawingGroup != null); + + drawingGroup.remove(d); + } + + /** + * Accessor for the drawing group + * + * @return the drawing group + */ + DrawingGroup getDrawingGroup() + { + return drawingGroup; + } + + /** + * Create a drawing group for this workbook - used when importing sheets + * which contain drawings, but this workbook doesn't. + * We can't subsume this into the getDrawingGroup() method because the + * null-ness of the return value is used elsewhere to determine the + * origin of the workbook + */ + DrawingGroup createDrawingGroup() + { + if (drawingGroup == null) + { + drawingGroup = new DrawingGroup(Origin.WRITE); + } + + return drawingGroup; + } + + /** + * Gets the named ranges + * + * @return the list of named cells within the workbook + */ + public String[] getRangeNames() + { + if (names == null) + { + return new String[0]; + } + + String[] n = new String[names.size()]; + for (int i = 0 ; i < names.size() ; i++) + { + NameRecord nr = (NameRecord) names.get(i); + n[i] = nr.getName(); + } + + return n; + } + + /** + * Removes the specified named range from the workbook + * + * @param name the name to remove + */ + public void removeRangeName(String name) + { + int pos = 0; + boolean found = false; + for (Iterator i = names.iterator(); i.hasNext() && !found ;) + { + NameRecord nr = (NameRecord) i.next(); + if (nr.getName().equals(name)) + { + found = true; + } + else + { + pos++; + } + } + + // Remove the name from the list of names and the associated hashmap + // of names (used to retrieve the name index). If the name cannot + // be found, a warning is displayed + if (found) + { + names.remove(pos); + if (nameRecords.remove(name) == null) + { + logger.warn("Could not remove " + name + " from index lookups"); + } + } + } + + /** + * Accessor for the common styles + * + * @return the standard styles for this workbook + */ + Styles getStyles() + { + return styles; + } + + /** + * Add new named area to this workbook with the given information. + * + * @param name name to be created. + * @param sheet sheet containing the name + * @param firstCol first column this name refers to. + * @param firstRow first row this name refers to. + * @param lastCol last column this name refers to. + * @param lastRow last row this name refers to. + */ + public void addNameArea(String name, + WritableSheet sheet, + int firstCol, + int firstRow, + int lastCol, + int lastRow) + { + addNameArea(name, sheet, firstCol, firstRow, lastCol, lastRow, true); + } + + /** + * Add new named area to this workbook with the given information. + * + * @param name name to be created. + * @param sheet sheet containing the name + * @param firstCol first column this name refers to. + * @param firstRow first row this name refers to. + * @param lastCol last column this name refers to. + * @param lastRow last row this name refers to. + * @param global TRUE if this is a global name, FALSE if this is tied to + * the sheet + */ + void addNameArea(String name, + WritableSheet sheet, + int firstCol, + int firstRow, + int lastCol, + int lastRow, + boolean global) + { + if (names == null) + { + names = new ArrayList(); + } + + int externalSheetIndex = getExternalSheetIndex(sheet.getName()); + + // Create a new name record. + NameRecord nr = + new NameRecord(name, + names.size(), + externalSheetIndex, + firstRow, lastRow, + firstCol, lastCol, + global); + + // Add new name to name array. + names.add(nr); + + // Add new name to name hash table. + nameRecords.put(name, nr); + } + + /** + * Add new named area to this workbook with the given information. + * + * @param name name to be created. + * @param sheet sheet containing the name + * @param firstCol first column this name refers to. + * @param firstRow first row this name refers to. + * @param lastCol last column this name refers to. + * @param lastRow last row this name refers to. + * @param global TRUE if this is a global name, FALSE if this is tied to + * the sheet + */ + void addNameArea(BuiltInName name, + WritableSheet sheet, + int firstCol, + int firstRow, + int lastCol, + int lastRow, + boolean global) + { + if (names == null) + { + names = new ArrayList(); + } + + int index = getInternalSheetIndex(sheet.getName()); + int externalSheetIndex = getExternalSheetIndex(sheet.getName()); + + // Create a new name record. + NameRecord nr = + new NameRecord(name, + index, + externalSheetIndex, + firstRow, lastRow, + firstCol, lastCol, + global); + + // Add new name to name array. + names.add(nr); + + // Add new name to name hash table. + nameRecords.put(name, nr); + } + + /** + * Add new named area to this workbook with the given information. + * + * @param name name to be created. + * @param sheet sheet containing the name + * @param firstCol first column this name refers to. + * @param firstRow first row this name refers to. + * @param lastCol last column this name refers to. + * @param lastRow last row this name refers to. + * @param firstCol2 first column this name refers to. + * @param firstRow2 first row this name refers to. + * @param lastCol2 last column this name refers to. + * @param lastRow2 last row this name refers to. + * @param global TRUE if this is a global name, FALSE if this is tied to + * the sheet + */ + void addNameArea(BuiltInName name, + WritableSheet sheet, + int firstCol, + int firstRow, + int lastCol, + int lastRow, + int firstCol2, + int firstRow2, + int lastCol2, + int lastRow2, + boolean global) + { + if (names == null) + { + names = new ArrayList(); + } + + int index = getInternalSheetIndex(sheet.getName()); + int externalSheetIndex = getExternalSheetIndex(sheet.getName()); + + // Create a new name record. + NameRecord nr = + new NameRecord(name, + index, + externalSheetIndex, + firstRow2, lastRow2, + firstCol2, lastCol2, + firstRow, lastRow, + firstCol, lastCol, + global); + + // Add new name to name array. + names.add(nr); + + // Add new name to name hash table. + nameRecords.put(name, nr); + } + + /** + * Accessor for the workbook settings + */ + WorkbookSettings getSettings() + { + return settings; + } + + /** + * Returns the cell for the specified location eg. "Sheet1!A4". + * This is identical to using the CellReferenceHelper with its + * associated performance overheads, consequently it should + * be use sparingly + * + * @param loc the cell to retrieve + * @return the cell at the specified location + */ + public WritableCell getWritableCell(String loc) + { + WritableSheet s = getSheet(CellReferenceHelper.getSheet(loc)); + return s.getWritableCell(loc); + } + + /** + * Imports a sheet from a different workbook. Does a deep copy on all + * elements within that sheet + * + * @param name the name of the new sheet + * @param index the position for the new sheet within this workbook + * @param sheet the sheet (from another workbook) to merge into this one + * @return the new sheet + */ + public WritableSheet importSheet(String name, int index, Sheet sheet) + { + WritableSheet ws = createSheet(name, index); + ((WritableSheetImpl) ws).importSheet(sheet); + + return ws; + } + +} + + + + + Index: 3rdParty_sources/jexcelapi/jxl/write/biff/WriteAccessRecord.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jexcelapi/jxl/write/biff/WriteAccessRecord.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jexcelapi/jxl/write/biff/WriteAccessRecord.java 17 Aug 2012 14:51:10 -0000 1.1 @@ -0,0 +1,72 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import jxl.Workbook; +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * The name used when Excel was installed. When writing worksheets, this + * is hard coded as Java Excel API + */ +class WriteAccessRecord extends WritableRecordData +{ + /** + * The data to output to file + */ + private byte[] data; + + // String of length 112 characters + /** + * The author of this workbook (ie. the Java Excel API) + */ + private final static String authorString = + "Java Excel API"; + + /** + * Constructor + */ + public WriteAccessRecord() + { + super(Type.WRITEACCESS); + + data = new byte[112]; + String astring = authorString + " v" + Workbook.getVersion(); + StringHelper.getBytes(astring, data, 0); + + // Pad out the record with space characters + for (int i = astring.length() ; i < data.length ;i++) + { + data[i] = 0x20; + } + } + + /** + * Gets the data for output to file + * + * @return the binary data + */ + public byte[] getData() + { + return data; + } +} Index: 3rdParty_sources/jgroups/org/jgroups/Address.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/Address.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/Address.java 17 Aug 2012 14:51:04 -0000 1.1 @@ -0,0 +1,27 @@ +// $Id: Address.java,v 1.1 2012/08/17 14:51:04 marcin Exp $ + +package org.jgroups; + +import org.jgroups.util.Streamable; + +import java.io.Externalizable; + + + +/** + * Abstract address. Used to identify members on a group to send messages to. + * Addresses are mostly generated by the bottom-most (transport) layers (e.g. UDP, TCP, LOOPBACK). + * @author Bela Ban + */ +public interface Address extends Externalizable, Streamable, Comparable, Cloneable { // todo: remove Externalizable + + /** + * Checks whether this is an address that represents multiple destinations; + * e.g., a class D address in the Internet. + * @return true if this is a multicast address, false if it is a unicast address + */ + boolean isMulticastAddress(); + + /** Returns serialized size of this address */ + int size(); +} Index: 3rdParty_sources/jgroups/org/jgroups/BlockEvent.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/BlockEvent.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/BlockEvent.java 17 Aug 2012 14:51:03 -0000 1.1 @@ -0,0 +1,10 @@ +// $Id: BlockEvent.java,v 1.1 2012/08/17 14:51:03 marcin Exp $ + +package org.jgroups; + +/** + * Trivial object that represents a block event. + */ +public class BlockEvent { + public String toString() {return "BlockEvent";} +} Index: 3rdParty_sources/jgroups/org/jgroups/Channel.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/Channel.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/Channel.java 17 Aug 2012 14:51:05 -0000 1.1 @@ -0,0 +1,567 @@ +// $Id: Channel.java,v 1.1 2012/08/17 14:51:05 marcin Exp $ + +package org.jgroups; + + +import org.apache.commons.logging.Log; + +import java.io.Serializable; +import java.util.*; +import java.util.concurrent.CopyOnWriteArraySet; + + +/** + A channel represents a group communication endpoint (like BSD datagram sockets). A + client joins a group by connecting the channel to a group address and leaves it by + disconnecting. Messages sent over the channel are received by all group members that + are connected to the same group (that is, all members that have the same group + address).

+ + The FSM for a channel is roughly as follows: a channel is created + (unconnected). The channel is connected to a group + (connected). Messages can now be sent and received. The channel is + disconnected from the group (unconnected). The channel could now be connected to a + different group again. The channel is closed (closed).

+ + Only a single sender is allowed to be connected to a channel at a time, but there can be + more than one channel in an application.

+ + Messages can be sent to the group members using the send method and messages + can be received using receive (pull approach).

+ + A channel instance is created using either a ChannelFactory or the public + constructor. Each implementation of a channel must provide a subclass of + Channel and an implementation of ChannelFactory.

+ Various degrees of sophistication in message exchange can be achieved using building + blocks on top of channels; e.g., light-weight groups, synchronous message invocation, + or remote method calls. Channels are on the same abstraction level as sockets, and + should really be simple to use. Higher-level abstractions are all built on top of + channels. + + @author Bela Ban + @see java.net.DatagramPacket + @see java.net.MulticastSocket + */ +public abstract class Channel implements Transport { + public static final int BLOCK=0; + public static final int VIEW=1; + public static final int SUSPECT=2; + public static final int LOCAL=3; + public static final int GET_STATE_EVENTS=4; + public static final int AUTO_RECONNECT=5; + public static final int AUTO_GETSTATE=6; + + + protected UpHandler up_handler=null; // when set, all events are passed to it ! + protected Set channel_listeners=null; + protected Receiver receiver=null; + + + protected abstract Log getLog(); + + /** + Connects the channel to a group. The client is now able to receive group + messages, views and block events (depending on the options set) and to send + messages to (all or single) group members. This is a null operation if already + connected.

+ + All channels with the same name form a group, that means all messages + sent to the group will be received by all channels connected to the same + channel name.

+ + @param cluster_name The name of the chanel to connect to. + @exception ChannelException The protocol stack cannot be started + @exception ChannelClosedException The channel is closed and therefore cannot be used any longer. + A new channel has to be created first. + @see Channel#disconnect + */ + abstract public void connect(String cluster_name) throws ChannelException; + + + /** + * Connects the channel to a group and fetches the state + * + * @param cluster_name + * The name of the cluster to connect to. + * @param target + * The address of the member from which the state is to be + * retrieved. If it is null, the state is retrieved from coordinator is contacted. + * @param state_id + * The ID of a substate. If the full state is to be fetched, set + * this parameter to null + * @param timeout + * Milliseconds to wait for the state response (0 = wait indefinitely). + * + * @throws ChannelException thrown if connecting to cluster was not successful + * @throws StateTransferException thrown if state transfer was not successful + * + */ + abstract public void connect(String cluster_name, Address target, String state_id, long timeout) throws ChannelException; + + + /** Disconnects the channel from the current group (if connected), leaving the group. + It is a null operation if not connected. It is a null operation if the channel is closed. + + @see #connect(String) */ + abstract public void disconnect(); + + + /** + Destroys the channel and its associated resources (e.g., the protocol stack). After a channel + has been closed, invoking methods on it throws the ChannelClosed exception + (or results in a null operation). It is a null operation if the channel is already closed.

+ If the channel is connected to a group, disconnec()t will be called first. + */ + abstract public void close(); + + + /** Shuts down the channel without disconnecting if connected, stops all the threads */ + abstract public void shutdown(); + + + /** + Re-opens a closed channel. Throws an exception if the channel is already open. After this method + returns, connect() may be called to join a group. The address of this member will be different from + the previous incarnation. + */ + public void open() throws ChannelException { + ; + } + + + /** + Determines whether the channel is open; + i.e., the protocol stack has been created (may not be connected though). + */ + abstract public boolean isOpen(); + + + /** + Determines whether the channel is connected to a group. This implies it is open. If true is returned, + then the channel can be used to send and receive messages. + */ + abstract public boolean isConnected(); + + + /** + * Returns the number of messages that are waiting. Those messages can be + * removed by {@link #receive(long)}. Note that this number could change after + * calling this method and before calling receive() (e.g. the latter + * method might be called by a different thread). + * @return The number of messages on the queue, or -1 if the queue/channel + * is closed/disconnected. + */ + public int getNumMessages() { + return -1; + } + + public String dumpQueue() { + return ""; + } + + + /** + * Returns a map of statistics of the various protocols and of the channel itself. + * @return Map. A map where the keys are the protocols ("channel" pseudo key is + * used for the channel itself") and the values are property maps. + */ + public abstract Map dumpStats(); + + /** Sends a message to a (unicast) destination. The message contains +

    +
  1. a destination address (Address). A null address sends the message + to all group members. +
  2. a source address. Can be left empty. Will be filled in by the protocol stack. +
  3. a byte buffer. The message contents. +
  4. several additional fields. They can be used by application programs (or patterns). E.g. + a message ID, a oneway field which determines whether a response is + expected etc. +
+ @param msg The message to be sent. Destination and buffer should be set. A null destination + means to send to all group members. + + @exception ChannelNotConnectedException The channel must be connected to send messages. + + @exception ChannelClosedException The channel is closed and therefore cannot be used any longer. + A new channel has to be created first. + + */ + abstract public void send(Message msg) throws ChannelNotConnectedException, ChannelClosedException; + + + /** + Helper method. Will create a Message(dst, src, obj) and use send(Message). + @param dst Destination address for message. If null, message will be sent to all current group members + @param src Source (sender's) address. If null, it will be set by the protocol's transport layer before + being put on the wire. Can usually be set to null. + @param obj Serializable object. Will be serialized into the byte buffer of the Message. If it is + not serializable, the byte buffer will be null. + */ + abstract public void send(Address dst, Address src, Serializable obj) throws ChannelNotConnectedException, + ChannelClosedException; + + + /** + Access to event mechanism of channels. Enables to send and receive events, used by building + blocks to communicate with (building block) specific protocol layers. Currently useful only + with JChannel. + */ + public void down(Event evt) { + } + + /** + * Can be used instead of down() when a return value is expected. This will be removed in 3.0 when we change + * the signature of down() to return Object rather than void + * @param evt + * @return + */ + public Object downcall(Event evt) { + return null; + } + + + /** Receives a message, a view change or a block event. By using setOpt, the + type of objects to be received can be determined (e.g., not views and blocks, just + messages). + + The possible types returned can be: +
    +
  1. Message. Normal message +
  2. Event. All other events (used by JChannel) +
  3. View. A view change. +
  4. BlockEvent. A block event indicating that a flush protocol has been started, and we should not + send any more messages. This event should be ack'ed by calling {@link org.jgroups.Channel#blockOk()} . + Any messages sent after blockOk() returns might get blocked until the flush protocol has completed. +
  5. UnblockEvent. An unblock event indicating that the flush protocol has completed and we can resume + sending messages +
  6. SuspectEvent. A notification of a suspected member. +
  7. GetStateEvent. The current state of the application should be + returned using ReturnState. +
  8. SetStateEvent. The state of a single/all members as requested previously + by having called Channel.getState(s). +
  9. ExitEvent. Signals that this member was forced to leave the group + (e.g., caused by the member being suspected.) The member can rejoin the group by calling + open(). If the AUTO_RECONNECT is set (see setOpt()), the reconnect will be done automatically. +
+ The instanceof operator can be used to discriminate between different types + returned. + @param timeout Value in milliseconds. Value <= 0 means wait forever + @return A Message, View, BlockEvent, SuspectEvent, GetStateEvent, SetStateEvent or + ExitEvent, depending on what is on top of the internal queue. + + @exception ChannelNotConnectedException The channel must be connected to receive messages. + + @exception ChannelClosedException The channel is closed and therefore cannot be used any longer. + A new channel has to be created first. + + @exception TimeoutException Thrown when a timeout has occurred. + @deprecated Use a {@link Receiver} instead + */ + abstract public Object receive(long timeout) throws ChannelNotConnectedException, + ChannelClosedException, TimeoutException; + + + /** Returns the next message, view, block, suspect or other event without removing + it from the queue. + @param timeout Value in milliseconds. Value <= 0 means wait forever + @return A Message, View, BlockEvent, SuspectEvent, GetStateEvent or SetStateEvent object, + depending on what is on top of the internal queue. + + @exception ChannelNotConnectedException The channel must be connected to receive messages. + + @exception ChannelClosedException The channel is closed and therefore cannot be used any longer. + A new channel has to be created first. + + @exception TimeoutException Thrown when a timeout has occurred. + + @see #receive(long) + @deprecated Use a {@link Receiver} instead, this method will not be available in JGroups 3.0 + */ + abstract public Object peek(long timeout) throws ChannelNotConnectedException, ChannelClosedException, TimeoutException; + + + /** + * Gets the current view. This does not retrieve a new view, use + receive() to do so. The view may only be available after a successful + connect(). The result of calling this method on an unconnected channel + is implementation defined (may return null). Calling it on a channel that is not + enabled to receive view events (via setOpt) returns + null. Calling this method on a closed channel returns a null view. + @return The current view. + */ + abstract public View getView(); + + + /** + Returns the channel's own address. The result of calling this method on an unconnected + channel is implementation defined (may return null). Calling this method on a closed + channel returns null. + + @return The channel's address. Generated by the underlying transport, and opaque. + Addresses can be used as destination in the Send operation. + */ + abstract public Address getLocalAddress(); + + + /** + Returns the group address of the group of which the channel is a member. This is + the object that was the argument to connect(). Calling this method on a closed + channel returns null. + @return The group address + @deprecated Use {@link #getClusterName()} instead */ + abstract public String getChannelName(); + + /** + Returns the cluster name of the group of which the channel is a member. This is + the object that was the argument to connect(). Calling this method on a closed + channel returns null. + @return The cluster name */ + abstract public String getClusterName(); + + + /** + When up_handler is set, all events will be passed to it directly. These will not be received + by the channel (except connect/disconnect, state retrieval and the like). This can be used by + building blocks on top of a channel; thus the channel is used as a pass-through medium, and + the building blocks take over some of the channel's tasks. However, tasks such as connection + management and state transfer is still handled by the channel. + */ + public void setUpHandler(UpHandler up_handler) { + this.up_handler=up_handler; + } + + + /** + Allows to be notified when a channel event such as connect, disconnect or close occurs. + E.g. a PullPushAdapter may choose to stop when the channel is closed, or to start when + it is opened. + @deprecated Use addChannelListener() instead + */ + public void setChannelListener(ChannelListener channel_listener) { + addChannelListener(channel_listener); + } + + /** + Allows to be notified when a channel event such as connect, disconnect or close occurs. + E.g. a PullPushAdapter may choose to stop when the channel is closed, or to start when + it is opened. + */ + public synchronized void addChannelListener(ChannelListener listener) { + if(listener == null) + return; + if(channel_listeners == null) + channel_listeners=new CopyOnWriteArraySet(); + channel_listeners.add(listener); + } + + public synchronized void removeChannelListener(ChannelListener listener) { + if(channel_listeners != null && listener != null) + channel_listeners.remove(listener); + } + + public synchronized void clearChannelListeners() { + if(channel_listeners != null) + channel_listeners.clear(); + } + + /** Sets the receiver, which will handle all messages, view changes etc */ + public void setReceiver(Receiver r) { + receiver=r; + } + + /** + Sets an option. The following options are currently recognized: +
    +
  1. BLOCK. Turn the reception of BLOCK events on/off (value is Boolean). + Default is off +
  2. LOCAL. Receive its own broadcast messages to the group + (value is Boolean). Default is on. +
  3. AUTO_RECONNECT. Turn auto-reconnection on/off. If on, when a member if forced out + of a group (EXIT event), then we will reconnect. +
  4. AUTO_GETSTATE. Turn automatic fetching of state after an auto-reconnect on/off. + This also sets AUTO_RECONNECT to true (if not yet set). +
+ This method can be called on an unconnected channel. Calling this method on a + closed channel has no effect. + */ + abstract public void setOpt(int option, Object value); + + + /** + Gets an option. This method can be called on an unconnected channel. Calling this + method on a closed channel returns null. + + @param option The option to be returned. + @return The object associated with an option. + */ + abstract public Object getOpt(int option); + + abstract public boolean flushSupported(); + + abstract public boolean startFlush(List
flushParticipants,boolean automatic_resume); + + abstract public boolean startFlush(boolean automatic_resume); + + abstract public boolean startFlush(long timeout, boolean automatic_resume); + + abstract public void stopFlush(); + + abstract public void stopFlush(List
flushParticipants); + + + /** Called to acknowledge a block() (callback in MembershipListener or + BlockEvent received from call to Receive). + After sending BlockOk, no messages should be sent until a new view has been received. + Calling this method on a closed channel has no effect. + */ + abstract public void blockOk(); + + + /** + Retrieve the state of the group. Will usually contact the oldest group member to get + the state. When the method returns true, a SetStateEvent will have been + added to the channel's queue, causing receive() to return the state in one of + the next invocations. If false, no state will be retrieved by receive(). + @param target The address of the member from which the state is to be retrieved. If it is + null, the coordinator is contacted. + @param timeout Milliseconds to wait for the response (0 = wait indefinitely). + @return boolean True if the state was retrieved successfully, otherwise false. + @exception ChannelNotConnectedException The channel must be connected to receive messages. + + @exception ChannelClosedException The channel is closed and therefore cannot be used + any longer. A new channel has to be created first. + + */ + abstract public boolean getState(Address target, long timeout) + throws ChannelNotConnectedException, ChannelClosedException; + + + /** + * Fetches a partial state identified by state_id. + * @param target + * @param state_id + * @param timeout + * @return + * @throws ChannelNotConnectedException + * @throws ChannelClosedException + */ + abstract public boolean getState(Address target, String state_id, long timeout) + throws ChannelNotConnectedException, ChannelClosedException; + + /** + Retrieve all states of the group members. Will contact all group members to get + the states. When the method returns true, a SetStateEvent will have been + added to the channel's queue, causing Receive to return the states in one of + the next invocations. If false, no states will be retrieved by Receive. + @param targets A list of members which are contacted for states. If the list is null, + all the current members of the group will be contacted. + @param timeout Milliseconds to wait for the response (0 = wait indefinitely). + @return boolean True if the state was retrieved successfully, otherwise false. + @exception ChannelNotConnectedException The channel must be connected to + receive messages. + @exception ChannelClosedException The channel is closed and therefore cannot be used + any longer. A new channel has to be created first. + @deprecated Not really needed - we always want to get the state from a single member + */ + abstract public boolean getAllStates(Vector targets, long timeout) + throws ChannelNotConnectedException, ChannelClosedException; + + + /** + * Called by the application is response to receiving a + * getState() object when calling receive(). + * @param state The state of the application as a byte buffer + * (to send over the network). + */ + public abstract void returnState(byte[] state); + + /** Returns a given substate (state_id of null means return entire state) */ + public abstract void returnState(byte[] state, String state_id); + + public abstract Map getInfo(); + public abstract void setInfo(String key, Object value); + + + public static String option2String(int option) { + switch(option) { + case BLOCK: + return "BLOCK"; + case VIEW: + return "VIEW"; + case SUSPECT: + return "SUSPECT"; + case LOCAL: + return "LOCAL"; + case GET_STATE_EVENTS: + return "GET_STATE_EVENTS"; + case AUTO_RECONNECT: + return "AUTO_RECONNECT"; + case AUTO_GETSTATE: + return "AUTO_GETSTATE"; + default: + return "unknown (" + option + ')'; + } + } + + protected void notifyChannelConnected(Channel c) { + if(channel_listeners == null) return; + for(ChannelListener channelListener: channel_listeners) { + try { + channelListener.channelConnected(c); + } + catch(Throwable t) { + getLog().error("exception in channelConnected() callback", t); + } + } + } + + protected void notifyChannelDisconnected(Channel c) { + if(channel_listeners == null) return; + for(ChannelListener channelListener: channel_listeners) { + try { + channelListener.channelDisconnected(c); + } + catch(Throwable t) { + getLog().error("exception in channelDisonnected() callback", t); + } + } + } + + protected void notifyChannelClosed(Channel c) { + if(channel_listeners == null) return; + for(ChannelListener channelListener: channel_listeners) { + try { + channelListener.channelClosed(c); + } + catch(Throwable t) { + getLog().error("exception in channelClosed() callback", t); + } + } + } + + protected void notifyChannelShunned() { + if(channel_listeners == null) return; + for(ChannelListener channelListener: channel_listeners) { + try { + channelListener.channelShunned(); + } + catch(Throwable t) { + getLog().error("exception in channelShunned() callback", t); + } + } + } + + protected void notifyChannelReconnected(Address addr) { + if(channel_listeners == null) return; + for(ChannelListener channelListener: channel_listeners) { + try { + channelListener.channelReconnected(addr); + } + catch(Throwable t) { + getLog().error("exception in channelReconnected() callback", t); + } + } + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/ChannelClosedException.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/ChannelClosedException.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/ChannelClosedException.java 17 Aug 2012 14:51:04 -0000 1.1 @@ -0,0 +1,23 @@ +// $Id: ChannelClosedException.java,v 1.1 2012/08/17 14:51:04 marcin Exp $ + +package org.jgroups; + +/** + * Thrown if an operation is attemped on a closed channel. + */ +public class ChannelClosedException extends ChannelException { + + private static final long serialVersionUID = -5172168752255182905L; + + public ChannelClosedException() { + super(); + } + + public ChannelClosedException(String msg) { + super(msg); + } + + public String toString() { + return "ChannelClosedException"; + } +} Index: 3rdParty_sources/jgroups/org/jgroups/ChannelException.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/ChannelException.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/ChannelException.java 17 Aug 2012 14:51:03 -0000 1.1 @@ -0,0 +1,25 @@ +// $Id: ChannelException.java,v 1.1 2012/08/17 14:51:03 marcin Exp $ + +package org.jgroups; + +/** + * This class represents the super class for all exception types thrown by + * JGroups. + */ +public class ChannelException extends Exception { + + private static final long serialVersionUID = 6041194633384856098L; + + public ChannelException() { + super(); + } + + public ChannelException(String reason) { + super(reason); + } + + public ChannelException(String reason, Throwable cause) { + super(reason, cause); + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/ChannelFactory.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/ChannelFactory.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/ChannelFactory.java 17 Aug 2012 14:51:04 -0000 1.1 @@ -0,0 +1,162 @@ +// $Id: ChannelFactory.java,v 1.1 2012/08/17 14:51:04 marcin Exp $ + +package org.jgroups; + +import org.w3c.dom.Element; + +import java.io.File; +import java.net.URL; + +/** + * A channel factory that removes hardwiring of calls to create JGroups + * channels. ChannelFactory enables client applications to use custom Channel + * creation methodologies. + * + * @see JChannelFactory + * + */ +public interface ChannelFactory { + + /** + * Initializes the factory. + * + * + * @param properties + * @throws ChannelException + */ + void setMultiplexerConfig(Object properties) throws Exception; + + /** + * Initializes the factory from a file. Example: conf/stacks.xml + * + * @param properties + * @throws ChannelException + */ + void setMultiplexerConfig(File properties) throws Exception; + + void setMultiplexerConfig(Element properties) throws Exception; + + void setMultiplexerConfig(URL properties) throws Exception; + + void setMultiplexerConfig(String properties) throws Exception; + + /** + * Creates an implementation of the Channel using a given stack name and + * registering it under a given identity. + *

+ * + * Channel has to be created with a unique application id per stack name. + * + *

+ * Provided stack name has to be one of the stacks defined in a property + * file that was passed to setMultiplexerConfig (e.g conf/stacks.xml). If + * clients attempt to create a Channel for an undefined stack name or + * they attempt to register a duplicate Channel per stack an Exception will be + * thrown. + * + * + *

+ * Rather than having each multiplexed channel do a separate state transfer + * clients can bundle state transfers for all channels created with the same + * ChannelFactory. First of all, clients have to create Channels with + * register_for_state_transfer set to true. After the last Channel that was + * created with register_for_state_transfer set to true connects and + * initiates state transfer the actual state transfer for all such channels + * from this ChannelFactory is executed. + * + *

+ * Using bundled state transfers is especially useful with the FLUSH + * protocol in a stack. Recall that each state transfer triggers a flush and + * thus instead of doing a separate flush for each Channel created with this + * ChannelFactory we execute only one flush for all state transfers. + * + *

+ * However, be aware of the implication of asynchronous nature of bundled + * state transfer with the respect of channel connect. Recall that each + * Channel after it returns from successful getState method can assume that + * state is available. In case of bundled state transfer, state will be set + * only after the last Channel registered for the bundled + * state transfer connects and executes getState. + * + * + * + * + * @param stack_name + * The name of the stack to be used. All stacks are defined in + * the configuration with which the factory is configured (see + * {@link #setMultiplexerConfig(Object)} for example. + * @param id + * The identifier used for multiplexing and demultiplexing + * (dispatching requests to one of possibly multiple receivers). + * Note that id needs to be a string since it will be shipped + * with each message. Try to pick a short string, because this is + * shipped with every message (overhead). + * @param register_for_state_transfer + * If set to true, after all registered listeners called + * either {@link Channel#connect(String, Address, String, long)} or + * {@link Channel#connect(String) and Channel#getState(Address, long)} + * successively on the returned Channel, the state for all + * registered listeners will be fetched and set in all listeners. + * @param substate_id + * The ID of the sub state to be retrieved. Set this to null if + * the entire state should be retrieved. If + * register_for_state_transfer is false, substate_id will be + * ignored + * + * + * @return An implementation of Channel which keeps track of the id, so that + * it can be attached to each message and be properly dispatched at + * the receiver. + * + * @see Multiplexer + * @see MuxChannel + * + * @throws ChannelException + */ + Channel createMultiplexerChannel(String stack_name, String id, boolean register_for_state_transfer, String substate_id) throws Exception; + + /** + * Creates an implementation of the Channel using a given stack name and + * registering it under a given identity. + *

+ * + * Channel has to be created with a unique application id per stack name. + * + *

+ * Provided stack name has to be one of the stacks defined in a property + * file that was passed to setMultiplexerConfig (e.g conf/stacks.xml). If + * clients attempt to create a Channel for an undefined stack name or + * they attempt to register a duplicate Channel per stack an Exception will be + * thrown. + * + * + * + * @param stack_name + * The name of the stack to be used. All stacks are defined in + * the configuration with which the factory is configured (see + * {@link #setMultiplexerConfig(Object)} for example. + * @param id + * The identifier used for multiplexing and demultiplexing + * (dispatching requests to one of possibly multiple receivers). + * Note that id needs to be a string since it will be shipped + * with each message. Try to pick a short string, because this is + * shipped with every message (overhead). + * + * @return An implementation of Channel which keeps track of the id, so that + * it can be attached to each message and be properly dispatched at + * the receiver. + * + * @see Multiplexer + * @see MuxChannel + * + * @throws ChannelException + */ + Channel createMultiplexerChannel(String stack_name, String id) throws Exception; + + Channel createChannel(Object props) throws ChannelException; + + /** Create a new channel with the properties defined in the factory */ + Channel createChannel() throws ChannelException; + + Channel createChannel(String stack_name) throws Exception; +} Index: 3rdParty_sources/jgroups/org/jgroups/ChannelListener.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/ChannelListener.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/ChannelListener.java 17 Aug 2012 14:51:05 -0000 1.1 @@ -0,0 +1,16 @@ +// $Id: ChannelListener.java,v 1.1 2012/08/17 14:51:05 marcin Exp $ + +package org.jgroups; + + +/** + * Allows a listener to be notified when important channel events occur. For example, when + * a channel is closed, a PullPushAdapter can be notified, and stop accordingly. + */ +public interface ChannelListener { + void channelConnected(Channel channel); + void channelDisconnected(Channel channel); + void channelClosed(Channel channel); + void channelShunned(); + void channelReconnected(Address addr); +} Index: 3rdParty_sources/jgroups/org/jgroups/ChannelListenerAdapter.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/ChannelListenerAdapter.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/ChannelListenerAdapter.java 17 Aug 2012 14:51:04 -0000 1.1 @@ -0,0 +1,23 @@ +package org.jgroups; + +/** + * Class which implements {@link org.jgroups.ChannelListener} + * @author Bela Ban + * @version $Id: ChannelListenerAdapter.java,v 1.1 2012/08/17 14:51:04 marcin Exp $ + */ +public class ChannelListenerAdapter implements ChannelListener { + public void channelConnected(Channel channel) { + } + + public void channelDisconnected(Channel channel) { + } + + public void channelClosed(Channel channel) { + } + + public void channelShunned() { + } + + public void channelReconnected(Address addr) { + } +} Index: 3rdParty_sources/jgroups/org/jgroups/ChannelNotConnectedException.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/ChannelNotConnectedException.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/ChannelNotConnectedException.java 17 Aug 2012 14:51:03 -0000 1.1 @@ -0,0 +1,22 @@ +// $Id: ChannelNotConnectedException.java,v 1.1 2012/08/17 14:51:03 marcin Exp $ + +package org.jgroups; + +/** + * Thrown if an operation is attemped on an unconnected channel. + */ +public class ChannelNotConnectedException extends ChannelException { + + private static final long serialVersionUID = -6701630538465783064L; + + public ChannelNotConnectedException() { + } + + public ChannelNotConnectedException(String reason) { + super(reason); + } + + public String toString() { + return "ChannelNotConnectedException"; + } +} Index: 3rdParty_sources/jgroups/org/jgroups/Event.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/Event.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/Event.java 17 Aug 2012 14:51:03 -0000 1.1 @@ -0,0 +1,159 @@ +// $Id: Event.java,v 1.1 2012/08/17 14:51:03 marcin Exp $ + +package org.jgroups; + + + +/** + * Used for inter-stack and intra-stack communication. + * @author Bela Ban + */ +public class Event { + public static final int MSG = 1; // arg = Message + public static final int CONNECT = 2; // arg = clster name (string) + public static final int DISCONNECT = 4; // arg = member address (Address) + public static final int VIEW_CHANGE = 6; // arg = View (or MergeView in case of merge) + public static final int SET_LOCAL_ADDRESS = 8; // arg = Address + public static final int SUSPECT = 9; // arg = Address of suspected member + public static final int BLOCK = 10; // arg = null (used by FLUSH) + public static final int FIND_INITIAL_MBRS = 12; // arg = JoinPromise (or null (merge2)) + public static final int MERGE = 14; // arg = Vector of Objects + public static final int TMP_VIEW = 15; // arg = View + public static final int BECOME_SERVER = 16; // sent when client has joined group + public static final int GET_APPLSTATE = 17; // get state from appl (arg=StateTransferInfo) + public static final int GET_STATE = 19; // arg = StateTransferInfo + public static final int GET_STATE_OK = 20; // arg = StateTransferInfo + public static final int STATE_RECEIVED = 21; // arg = StateTransferInfo (with state and state_id) + public static final int STABLE = 30; // arg = long[] (stable seqnos for mbrs) + public static final int GET_DIGEST = 39; // + public static final int SET_DIGEST = 41; // arg = Digest + public static final int EXIT = 46; // received when member was forced out of the group + public static final int PERF = 47; // for performance measurements + public static final int HEARD_FROM = 50; // arg = Vector (list of Addresses) + public static final int UNSUSPECT = 51; // arg = Address (of unsuspected member) + public static final int MERGE_DIGEST = 53; // arg = Digest + public static final int CONFIG = 56; // arg = Map (config properties) + public static final int SUSPEND_STABLE = 65; // arg = Long (max_suspend_time) + public static final int RESUME_STABLE = 66; // arg = null + public static final int ENABLE_UNICASTS_TO = 67; // arg = Address (member) + public static final int SUSPEND = 68; // arg = HashMap (used by FLUSH) + public static final int RESUME = 70; // arg = null (used by FLUSH) + public static final int STATE_TRANSFER_INPUTSTREAM = 71; // arg=java.io.InputStream subclass + public static final int STATE_TRANSFER_OUTPUTSTREAM = 72; // arg=java.io.OutputStream subclass + public static final int STATE_TRANSFER_INPUTSTREAM_CLOSED = 73; //arg=null + public static final int STATE_TRANSFER_OUTPUTSTREAM_CLOSED = 74; //arg=null + public static final int UNBLOCK = 75; //arg=null (indicate end of flush round) + public static final int CLOSE_BARRIER = 76; // arg = null + public static final int OPEN_BARRIER = 77; // arg = null + public static final int REBROADCAST = 78; // arg = Digest + public static final int SHUTDOWN = 79; // arg = null (shutdown without closing sockets or cleaning up) + public static final int CONNECT_WITH_STATE_TRANSFER = 80; // arg = cluster name (string) + public static final int DISABLE_UNICASTS_TO = 81; // arg = Address (member) + public static final int START_PARTITION = 82; // arg = null; + public static final int STOP_PARTITION = 83; // arg = null; + public static final int INFO = 84; // arg = Map + public static final int PREPARE_VIEW = 86; // arg = View + + public static final int USER_DEFINED = 1000; // arg = + + + private final int type; // type of event + private final Object arg; // must be serializable if used for inter-stack communication + + + public Event(int type) { + this.type=type; + this.arg=null; + } + + public Event(int type, Object arg) { + this.type=type; + this.arg=arg; + } + + public final int getType() { + return type; + } + + /** + * Sets the new type + * @param type + * @deprecated in order to make an Event immutable + */ + public void setType(int type) { + throw new IllegalAccessError("setType() has been deprecated, to make Events immutable"); + } + + public Object getArg() { + return arg; + } + + public void setArg(Object arg) { + throw new IllegalAccessError("setArg() has been deprecated, to make Events immutable"); + } + + + + public static String type2String(int t) { + switch(t) { + case MSG: return "MSG"; + case CONNECT: return "CONNECT"; + case DISCONNECT: return "DISCONNECT"; + case VIEW_CHANGE: return "VIEW_CHANGE"; + case SET_LOCAL_ADDRESS: return "SET_LOCAL_ADDRESS"; + case SUSPECT: return "SUSPECT"; + case BLOCK: return "BLOCK"; + case FIND_INITIAL_MBRS: return "FIND_INITIAL_MBRS"; + case TMP_VIEW: return "TMP_VIEW"; + case BECOME_SERVER: return "BECOME_SERVER"; + case GET_APPLSTATE: return "GET_APPLSTATE"; + case GET_STATE: return "GET_STATE"; + case GET_STATE_OK: return "GET_STATE_OK"; + case STATE_RECEIVED: return "STATE_RECEIVED"; + case STABLE: return "STABLE"; + case GET_DIGEST: return "GET_DIGEST"; + case SET_DIGEST: return "SET_DIGEST"; + case MERGE: return "MERGE"; // Added by gianlucac@tin.it to support partitions merging in GMS + case EXIT: return "EXIT"; + case PERF: return "PERF"; + case HEARD_FROM: return "HEARD_FROM"; + case UNSUSPECT: return "UNSUSPECT"; + case MERGE_DIGEST: return "MERGE_DIGEST"; + case CONFIG: return "CONFIG"; + case SUSPEND_STABLE: return "SUSPEND_STABLE"; + case RESUME_STABLE: return "RESUME_STABLE"; + case ENABLE_UNICASTS_TO: return "ENABLE_UNICASTS_TO"; + case SUSPEND: return "SUSPEND"; + case RESUME: return "RESUME"; + case STATE_TRANSFER_INPUTSTREAM: return "STATE_TRANSFER_INPUTSTREAM"; + case STATE_TRANSFER_OUTPUTSTREAM:return "STATE_TRANSFER_OUTPUTSTREAM"; + case STATE_TRANSFER_INPUTSTREAM_CLOSED: return "STATE_TRANSFER_INPUTSTREAM_CLOSED"; + case STATE_TRANSFER_OUTPUTSTREAM_CLOSED: return "STATE_TRANSFER_OUTPUTSTREAM_CLOSED"; + case UNBLOCK: return "UNBLOCK"; + case CLOSE_BARRIER: return "CLOSE_BARRIER"; + case OPEN_BARRIER: return "OPEN_BARRIER"; + case REBROADCAST: return "REBROADCAST"; + case SHUTDOWN: return "SHUTDOWN"; + case CONNECT_WITH_STATE_TRANSFER: return "CONNECT_WITH_STATE_TRANSFER"; + case DISABLE_UNICASTS_TO: return "DISABLE_UNICASTS_TO"; + case START_PARTITION: return "START_PARTITION"; + case STOP_PARTITION: return "STOP_PARTITION"; + case INFO: return "INFO"; + case PREPARE_VIEW: return "PREPARE_VIEW"; + case USER_DEFINED: return "USER_DEFINED"; + default: return "UNDEFINED(" + t + ")"; + } + } + + public static final Event GET_DIGEST_EVT = new Event(Event.GET_DIGEST); + + public String toString() { + StringBuilder ret=new StringBuilder(64); + ret.append("Event[type=" + type2String(type) + ", arg=" + arg + ']'); + if(type == MSG) + ret.append(" (headers=").append(((Message)arg).printHeaders()).append(")"); + return ret.toString(); + } + +} + Index: 3rdParty_sources/jgroups/org/jgroups/ExitEvent.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/ExitEvent.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/ExitEvent.java 17 Aug 2012 14:51:04 -0000 1.1 @@ -0,0 +1,9 @@ +// $Id: ExitEvent.java,v 1.1 2012/08/17 14:51:04 marcin Exp $ + +package org.jgroups; +/** + * Trivial object that represents an exit event. + */ +public class ExitEvent { + public String toString() {return "ExitEvent";} +} Index: 3rdParty_sources/jgroups/org/jgroups/ExtendedMembershipListener.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/ExtendedMembershipListener.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/ExtendedMembershipListener.java 17 Aug 2012 14:51:03 -0000 1.1 @@ -0,0 +1,14 @@ +package org.jgroups; + +/** + * @author Bela Ban + * @version $Id: ExtendedMembershipListener.java,v 1.1 2012/08/17 14:51:03 marcin Exp $ + */ +public interface ExtendedMembershipListener extends MembershipListener { + + /** + * Called after the FLUSH protocol has unblocked previously blocked senders, and messages can be sent again. This + * callback only needs to be implemented if we require a notification of that. + */ + void unblock(); +} Index: 3rdParty_sources/jgroups/org/jgroups/ExtendedMessageListener.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/ExtendedMessageListener.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/ExtendedMessageListener.java 17 Aug 2012 14:51:04 -0000 1.1 @@ -0,0 +1,91 @@ +package org.jgroups; + +import java.io.InputStream; +import java.io.OutputStream; + +/** + * + * ExtendedMessageListener has additional callbacks for: + *

    + *
  • partial state transfer - http://jira.jboss.com/jira/browse/JGRP-118 + *
  • streaming state transfer - http://jira.jboss.com/jira/browse/JGRP-89 + *
+ *

+ * Application channels interested in using streaming state transfer, beside + * implementing this interface, have to be configured with + * STREAMING_STATE_TRANSFER protocol rather than the default + * STATE_TRANSFER protocol. + * + *

+ * Note: + *

+ * This interface will be merged with MessageListener in 3.0 (API changes) + * + * @author Bela Ban + * @author Vladimir Blagojevic + * @see org.jgroups.JChannel#getState(Address, long) + * @see org.jgroups.JChannel#getState(Address, String, long) + * @since 2.3 + * + * @version $Id: ExtendedMessageListener.java,v 1.1 2012/08/17 14:51:04 marcin Exp $ + */ +public interface ExtendedMessageListener extends MessageListener { + + /** + * Allows an application to provide a partial state as a byte array + * + * @param state_id id of the partial state requested + * @return partial state for the given state_id + */ + public byte[] getState(String state_id); + + /** + * Allows an application to read a partial state indicated by state_id from + * a given state byte array parameter. + * + * @param state_id id of the partial state requested + * @param state partial state for the given state_id + */ + public void setState(String state_id, byte[] state); + + /** + * Allows an application to write a state through a provided OutputStream. + * An application is obligated to always close the given OutputStream reference. + * + * @param ostream the OutputStream + * @see OutputStream#close() + */ + public void getState(OutputStream ostream); + + /** + * Allows an application to write a partial state through a provided OutputStream. + * An application is obligated to always close the given OutputStream reference. + * + * @param state_id id of the partial state requested + * @param ostream the OutputStream + * + * @see OutputStream#close() + */ + public void getState(String state_id, OutputStream ostream); + + + /** + * Allows an application to read a state through a provided InputStream. + * An application is obligated to always close the given InputStream reference. + * + * @param istream the InputStream + * @see InputStream#close() + */ + public void setState(InputStream istream); + + /** + * Allows an application to read a partial state through a provided InputStream. + * An application is obligated to always close the given InputStream reference. + * + * @param state_id id of the partial state requested + * @param istream the InputStream + * + * @see InputStream#close() + */ + public void setState(String state_id, InputStream istream); +} Index: 3rdParty_sources/jgroups/org/jgroups/ExtendedReceiver.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/ExtendedReceiver.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/ExtendedReceiver.java 17 Aug 2012 14:51:05 -0000 1.1 @@ -0,0 +1,10 @@ +package org.jgroups; + +/** + * Extends Receiver, plus the partial state transfer methods. + * This interface will disappear (be merged with Receiver) in 3.0. + * @author Bela Ban + * @version $Id: ExtendedReceiver.java,v 1.1 2012/08/17 14:51:05 marcin Exp $ + */ +public interface ExtendedReceiver extends Receiver, ExtendedMessageListener, ExtendedMembershipListener { +} Index: 3rdParty_sources/jgroups/org/jgroups/ExtendedReceiverAdapter.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/ExtendedReceiverAdapter.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/ExtendedReceiverAdapter.java 17 Aug 2012 14:51:05 -0000 1.1 @@ -0,0 +1,57 @@ +package org.jgroups; + +import java.io.InputStream; +import java.io.OutputStream; + +import org.jgroups.util.Util; + +/** + * @author Bela Ban + * @version $Id: ExtendedReceiverAdapter.java,v 1.1 2012/08/17 14:51:05 marcin Exp $ + */ +public class ExtendedReceiverAdapter implements ExtendedReceiver { + public byte[] getState(String state_id) { + return null; + } + + public void setState(String state_id, byte[] state) { + } + + public void receive(Message msg) { + } + + public byte[] getState() { + return null; + } + + public void setState(byte[] state) { + } + + public void viewAccepted(View new_view) { + } + + public void suspect(Address suspected_mbr) { + } + + public void block() { + } + + public void unblock() { + } + + public void getState(OutputStream ostream) { + Util.close(ostream); + } + + public void getState(String state_id, OutputStream ostream) { + Util.close(ostream); + } + + public void setState(InputStream istream) { + Util.close(istream); + } + + public void setState(String state_id, InputStream istream) { + Util.close(istream); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/GetStateEvent.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/GetStateEvent.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/GetStateEvent.java 17 Aug 2012 14:51:03 -0000 1.1 @@ -0,0 +1,23 @@ +// $Id: GetStateEvent.java,v 1.1 2012/08/17 14:51:03 marcin Exp $ + +package org.jgroups; + +/** + * Represents a GetState event. + * Gives access to the requestor. + */ +public class GetStateEvent { + Object requestor=null; + String state_id=null; + + public GetStateEvent(Object requestor, String state_id) { + this.requestor=requestor; + this.state_id=state_id; + } + + public Object getRequestor() {return requestor;} + + public String getStateId() {return state_id;} + + public String toString() {return "GetStateEvent[requestor=" + requestor + ", state_id=" + state_id + ']';} +} Index: 3rdParty_sources/jgroups/org/jgroups/Global.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/Global.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/Global.java 17 Aug 2012 14:51:03 -0000 1.1 @@ -0,0 +1,102 @@ +package org.jgroups; + +/** + * Globals used by JGroups packages. + * + * @author Bela Ban Mar 29, 2004 + * @version $Id: Global.java,v 1.1 2012/08/17 14:51:03 marcin Exp $ + */ +public class Global { + /** Allows for conditional compilation; e.g., if(log.isTraceEnabled()) if(log.isInfoEnabled()) log.info(...) would be removed from the code + (if recompiled) when this flag is set to false. Therefore, code that should be removed from the final + product should use if(log.isTraceEnabled()) rather than . + */ + public static final boolean debug=false; + + public static final String THREAD_PREFIX=" (channel="; + + public static final int BYTE_SIZE = Byte.SIZE / 8; // 1 + public static final int SHORT_SIZE = Short.SIZE / 8; // 2 + public static final int INT_SIZE = Integer.SIZE / 8; // 4 + public static final int LONG_SIZE = Long.SIZE / 8; // 8 + + public static final Object NULL=new Object(); + + public static final String BIND_ADDR="jgroups.bind_addr"; + public static final String BIND_ADDR_OLD="bind.address"; + public static final String BIND_INTERFACE="jgroups.bind_interface"; + public static final String IGNORE_BIND_ADDRESS_PROPERTY="jgroups.ignore.bind_addr"; + public static final String IGNORE_BIND_ADDRESS_PROPERTY_OLD="ignore.bind.address"; + public static final String MARSHALLING_COMPAT="jgroups.marshalling.compatible"; + + public static final String TCPPING_INITIAL_HOSTS="jgroups.tcpping.initial_hosts"; + + public static final String UDP_MCAST_ADDR="jgroups.udp.mcast_addr"; + public static final String UDP_MCAST_PORT="jgroups.udp.mcast_port"; + public static final String UDP_IP_TTL="jgroups.udp.ip_ttl"; + + public static final String MPING_MCAST_ADDR="jgroups.mping.mcast_addr"; + public static final String MPING_MCAST_PORT="jgroups.mping.mcast_port"; + public static final String MPING_IP_TTL="jgroups.mping.ip_ttl"; + + public static final String MAGIC_NUMBER_FILE="jgroups.conf.magic_number_file"; + public static final String RESOLVE_DNS="jgroups.resolve_dns"; + + public static final String CHANNEL_LOCAL_ADDR_TIMEOUT="jgroups.channel.local_addr_timeout"; + + public static final String SCHEDULER_MAX_THREADS="jgroups.scheduler.max_threads"; + + public static final String TIMER_NUM_THREADS="jgroups.timer.num_threads"; + + public static final String MUX_ENABLED="jgroups.mux.enabled"; + public static final String MUX_MIN_THREADS="jgroups.mux.min_threads"; + public static final String MUX_MAX_THREADS="jgroups.mux.max_threads"; + public static final String MUX_KEEPALIVE="jgroups.mux.keepalive_time"; + + + public static final String SINGLETON_NAME="singleton_name"; + + public static final long THREADPOOL_SHUTDOWN_WAIT_TIME=3000; + public static final long THREAD_SHUTDOWN_WAIT_TIME=300; + public static final String DUMMY="dummy-"; + + + public static final int IPV4_SIZE=4; + public static final int IPV6_SIZE=16; + + public static boolean getPropertyAsBoolean(String property, boolean defaultValue) { + boolean result=defaultValue; + try { + String tmp=System.getProperty(property); + if(tmp != null) + result=Boolean.parseBoolean(tmp); + } + catch(Throwable t) { + } + return result; + } + + public static long getPropertyAsLong(String property, long defaultValue) { + long result=defaultValue; + try { + String tmp=System.getProperty(property); + if(tmp != null) + result=Long.parseLong(tmp); + } + catch(Throwable t) { + } + return result; + } + + public static int getPropertyAsInteger(String property, int defaultValue) { + int result=defaultValue; + try { + String tmp=System.getProperty(property); + if(tmp != null) + result=Integer.parseInt(tmp); + } + catch(Throwable t) { + } + return result; + } +} Index: 3rdParty_sources/jgroups/org/jgroups/Header.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/Header.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/Header.java 17 Aug 2012 14:51:04 -0000 1.1 @@ -0,0 +1,45 @@ +// $Id: Header.java,v 1.1 2012/08/17 14:51:04 marcin Exp $ + +package org.jgroups; + +import java.io.Externalizable; + + +/** + Abstract base class for all headers to be added to a Message. + @author Bela Ban + */ +public abstract class Header implements Externalizable { + public static final int HDR_OVERHEAD=100; // estimated size of a header (used to estimate the size of the entire msg) + + + public Header() { + + } + + + /** + * To be implemented by subclasses. Return the size of this object for the serialized version of it. + * I.e. how many bytes this object takes when flattened into a buffer. This may be different for each instance, + * or can be the same. This may also just be an estimation. E.g. FRAG uses it on Message to determine whether + * or not to fragment the message. Fragmentation itself will be accurate, because the entire message will actually + * be serialized into a byte buffer, so we can determine the exact size. + */ + public int size() { + return HDR_OVERHEAD; + } + +// public void writeTo(DataOutputStream out) throws IOException { +// ; +// } +// +// public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { +// ; +// } + + + public String toString() { + return '[' + getClass().getName() + " Header]"; + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/JChannel.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/JChannel.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/JChannel.java 17 Aug 2012 14:51:03 -0000 1.1 @@ -0,0 +1,2078 @@ +package org.jgroups; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.conf.ConfiguratorFactory; +import org.jgroups.conf.ProtocolStackConfigurator; +import org.jgroups.stack.IpAddress; +import org.jgroups.stack.ProtocolStack; +import org.jgroups.stack.StateTransferInfo; +import org.jgroups.util.*; +import org.jgroups.protocols.TP; +import org.w3c.dom.Element; + +import java.io.File; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Serializable; +import java.net.URL; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Vector; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.Exchanger; + +/** + * JChannel is a pure Java implementation of Channel. + * When a JChannel object is instantiated it automatically sets up the + * protocol stack. + *

+ * Properties + *

+ * Properties are used to configure a channel, and are accepted in + * several forms; the String form is described here. + * A property string consists of a number of properties separated by + * colons. For example: + *

+ *

"<prop1>(arg1=val1):<prop2>(arg1=val1;arg2=val2):<prop3>:<propn>"
+ *

+ * Each property relates directly to a protocol layer, which is + * implemented as a Java class. When a protocol stack is to be created + * based on the above property string, the first property becomes the + * bottom-most layer, the second one will be placed on the first, etc.: + * the stack is created from the bottom to the top, as the string is + * parsed from left to right. Each property has to be the name of a + * Java class that resides in the + * {@link org.jgroups.protocols} package. + *

+ * Note that only the base name has to be given, not the fully specified + * class name (e.g., UDP instead of org.jgroups.protocols.UDP). + *

+ * Each layer may have 0 or more arguments, which are specified as a + * list of name/value pairs in parentheses directly after the property. + * In the example above, the first protocol layer has 1 argument, + * the second 2, the third none. When a layer is created, these + * properties (if there are any) will be set in a layer by invoking + * the layer's setProperties() method + *

+ * As an example the property string below instructs JGroups to create + * a JChannel with protocols UDP, PING, FD and GMS:

+ *

"UDP(mcast_addr=228.10.9.8;mcast_port=5678):PING:FD:GMS"
+ *

+ * The UDP protocol layer is at the bottom of the stack, and it + * should use mcast address 228.10.9.8. and port 5678 rather than + * the default IP multicast address and port. The only other argument + * instructs FD to output debug information while executing. + * Property UDP refers to a class {@link org.jgroups.protocols.UDP}, + * which is subsequently loaded and an instance of which is created as protocol layer. + * If any of these classes are not found, an exception will be thrown and + * the construction of the stack will be aborted. + * + * @author Bela Ban + * @version $Id: JChannel.java,v 1.1 2012/08/17 14:51:03 marcin Exp $ + */ +public class JChannel extends Channel { + + /** + * The default protocol stack used by the default constructor. + */ + public static final String DEFAULT_PROTOCOL_STACK="udp.xml"; + + static final String FORCE_PROPS="force.properties"; + + /* the protocol stack configuration string */ + private String props=null; + + /*the address of this JChannel instance*/ + private Address local_addr=null; + /*the channel (also know as group) name*/ + private String cluster_name=null; // group name + /*the latest view of the group membership*/ + private View my_view=null; + /*the queue that is used to receive messages (events) from the protocol stack*/ + private final Queue mq=new Queue(); + /*the protocol stack, used to send and receive messages from the protocol stack*/ + private ProtocolStack prot_stack=null; + + /** Thread responsible for closing a channel and potentially reconnecting to it (e.g., when shunned). */ + protected CloserThread closer=null; + + /** To wait until a local address has been assigned */ + private final Promise

local_addr_promise=new Promise
(); + + private final Promise state_promise=new Promise(); + + private final Exchanger applstate_exchanger=new Exchanger(); + + private final Promise flush_unblock_promise=new Promise(); + + /** wait until we have a non-null local_addr */ + private long LOCAL_ADDR_TIMEOUT=30000; //=Long.parseLong(System.getProperty("local_addr.timeout", "30000")); + /*if the states is fetched automatically, this is the default timeout, 5 secs*/ + private static final long GET_STATE_DEFAULT_TIMEOUT=5000; + /*if FLUSH is used channel waits for UNBLOCK event, this is the default timeout, 5 secs*/ + private static final long FLUSH_UNBLOCK_TIMEOUT=5000; + /*flag to indicate whether to receive blocks, if this is set to true, receive_views is set to true*/ + private boolean receive_blocks=false; + /*flag to indicate whether to receive local messages + *if this is set to false, the JChannel will not receive messages sent by itself*/ + private boolean receive_local_msgs=true; + /*flag to indicate whether the channel will reconnect (reopen) when the exit message is received*/ + private boolean auto_reconnect=true; + /*flag t indicate whether the state is supposed to be retrieved after the channel is reconnected + *setting this to true, automatically forces auto_reconnect to true*/ + private boolean auto_getstate=false; + /*channel connected flag*/ + protected volatile boolean connected=false; + + /*channel closed flag*/ + protected volatile boolean closed=false; // close() has been called, channel is unusable + + /** True if a state transfer protocol is available, false otherwise */ + private boolean state_transfer_supported=false; // set by CONFIG event from STATE_TRANSFER protocol + + /** True if a flush protocol is available, false otherwise */ + private volatile boolean flush_supported=false; // set by CONFIG event from FLUSH protocol + + /** Provides storage for arbitrary objects. Protocols can send up CONFIG events, and all key-value pairs of + * a CONFIG event will be added to additional_data. On reconnect, a CONFIG event will be sent down by the channel, + * containing all key-value pairs of additional_data + */ + protected final Map additional_data=new HashMap(); + + protected final ConcurrentMap info=new ConcurrentHashMap(); + + protected final Log log=LogFactory.getLog(getClass()); + + /** Collect statistics */ + protected boolean stats=true; + + protected long sent_msgs=0, received_msgs=0, sent_bytes=0, received_bytes=0; + + private final TP.ProbeHandler probe_handler=new MyProbeHandler(); + + + + /** Used by subclass to create a JChannel without a protocol stack, don't use as application programmer */ + protected JChannel(boolean no_op) { + ; + } + + /** + * Constructs a JChannel instance with the protocol stack + * specified by the DEFAULT_PROTOCOL_STACK member. + * + * @throws ChannelException if problems occur during the initialization of + * the protocol stack. + */ + public JChannel() throws ChannelException { + this(DEFAULT_PROTOCOL_STACK); + } + + /** + * Constructs a JChannel instance with the protocol stack + * configuration contained by the specified file. + * + * @param properties a file containing a JGroups XML protocol stack + * configuration. + * + * @throws ChannelException if problems occur during the configuration or + * initialization of the protocol stack. + */ + public JChannel(File properties) throws ChannelException { + this(ConfiguratorFactory.getStackConfigurator(properties)); + } + + /** + * Constructs a JChannel instance with the protocol stack + * configuration contained by the specified XML element. + * + * @param properties a XML element containing a JGroups XML protocol stack + * configuration. + * + * @throws ChannelException if problems occur during the configuration or + * initialization of the protocol stack. + */ + public JChannel(Element properties) throws ChannelException { + this(ConfiguratorFactory.getStackConfigurator(properties)); + } + + /** + * Constructs a JChannel instance with the protocol stack + * configuration indicated by the specified URL. + * + * @param properties a URL pointing to a JGroups XML protocol stack + * configuration. + * + * @throws ChannelException if problems occur during the configuration or + * initialization of the protocol stack. + */ + public JChannel(URL properties) throws ChannelException { + this(ConfiguratorFactory.getStackConfigurator(properties)); + } + + /** + * Constructs a JChannel instance with the protocol stack + * configuration based upon the specified properties parameter. + * + * @param properties an old style property string, a string representing a + * system resource containing a JGroups XML configuration, + * a string representing a URL pointing to a JGroups XML + * XML configuration, or a string representing a file name + * that contains a JGroups XML configuration. + * + * @throws ChannelException if problems occur during the configuration and + * initialization of the protocol stack. + */ + public JChannel(String properties) throws ChannelException { + this(ConfiguratorFactory.getStackConfigurator(properties)); + } + + /** + * Constructs a JChannel instance with the protocol stack + * configuration contained by the protocol stack configurator parameter. + *

+ * All of the public constructors of this class eventually delegate to this + * method. + * + * @param configurator a protocol stack configurator containing a JGroups + * protocol stack configuration. + * + * @throws ChannelException if problems occur during the initialization of + * the protocol stack. + */ + public JChannel(ProtocolStackConfigurator configurator) throws ChannelException { + init(configurator); + } + + + + + /** + * Creates a new JChannel with the protocol stack as defined in the properties + * parameter. an example of this parameter is
+ * "UDP:PING:FD:STABLE:NAKACK:UNICAST:FRAG:FLUSH:GMS:VIEW_ENFORCER:STATE_TRANSFER:QUEUE"
+ * Other examples can be found in the ./conf directory
+ * @param properties the protocol stack setup; if null, the default protocol stack will be used. + * The properties can also be a java.net.URL object or a string that is a URL spec. + * The JChannel will validate any URL object and String object to see if they are a URL. + * In case of the parameter being a url, the JChannel will try to load the xml from there. + * In case properties is a org.w3c.dom.Element, the ConfiguratorFactory will parse the + * DOM tree with the element as its root element. + * @deprecated Use the constructors with specific parameter types instead. + */ + public JChannel(Object properties) throws ChannelException { + if (properties == null) + properties = DEFAULT_PROTOCOL_STACK; + + ProtocolStackConfigurator c=null; + + try { + c=ConfiguratorFactory.getStackConfigurator(properties); + } + catch(Exception x) { + throw new ChannelException("unable to load protocol stack", x); + } + init(c); + } + + + /** + * Returns the protocol stack. + * Currently used by Debugger. + * Specific to JChannel, therefore + * not visible in Channel + */ + public ProtocolStack getProtocolStack() { + return prot_stack; + } + + protected Log getLog() { + return log; + } + + /** + * returns the protocol stack configuration in string format. + * an example of this property is
+ * "UDP:PING:FD:STABLE:NAKACK:UNICAST:FRAG:FLUSH:GMS:VIEW_ENFORCER:STATE_TRANSFER:QUEUE" + */ + public String getProperties() { + return props; + } + + public boolean statsEnabled() { + return stats; + } + + public void enableStats(boolean stats) { + this.stats=stats; + } + + public void resetStats() { + sent_msgs=received_msgs=sent_bytes=received_bytes=0; + } + + public long getSentMessages() {return sent_msgs;} + public long getSentBytes() {return sent_bytes;} + public long getReceivedMessages() {return received_msgs;} + public long getReceivedBytes() {return received_bytes;} + public int getNumberOfTasksInTimer() { + TimeScheduler timer=getTimer(); + return timer != null? timer.size() : -1; + } + + public int getTimerThreads() { + TimeScheduler timer=getTimer(); + return timer != null? timer.getCorePoolSize() : -1; + } + + public String dumpTimerQueue() { + TimeScheduler timer=getTimer(); + return timer != null? timer.dumpTaskQueue() : "String
denoting the group name. Cannot be null. + * @exception ChannelException The protocol stack cannot be started + * @exception ChannelClosedException The channel is closed and therefore cannot be used any longer. + * A new channel has to be created first. + */ + public synchronized void connect(String cluster_name) throws ChannelException { + if(connected) { + if(log.isTraceEnabled()) log.trace("already connected to " + cluster_name); + return; + } + + startStack(cluster_name); + + if(cluster_name != null) { // only connect if we are not a unicast channel + + Event connect_event=new Event(Event.CONNECT, cluster_name); + Object res=downcall(connect_event); // waits forever until connected (or channel is closed) + if(res != null && res instanceof Exception) { // the JOIN was rejected by the coordinator + stopStack(true, false); + init(); + throw new ChannelException("connect() failed", (Throwable)res); + } + + //if FLUSH is used do not return from connect() until UNBLOCK event is received + if(flushSupported()) { + try { + flush_unblock_promise.getResultWithTimeout(FLUSH_UNBLOCK_TIMEOUT); + } + catch (TimeoutException timeout) { + if(log.isWarnEnabled()) + log.warn(local_addr + " waiting on UNBLOCK after connect() timed out"); + } + } + } + connected=true; + notifyChannelConnected(this); + } + + + /** + * Connects this channel to a group and gets a state from a specified state + * provider. + *

+ * + * This method essentially invokes + * connect and getState methods successively. + * If FLUSH protocol is in channel's stack definition only one flush is executed for both connecting and + * fetching state rather than two flushes if we invoke connect and getState in succesion. + * + * If the channel is already connected, an error message will be printed to the error log. + * If the channel is closed a ChannelClosed exception will be thrown. + * + * + * @param cluster_name the cluster name to connect to. Cannot be null. + * @param target the state provider. If null state will be fetched from coordinator, unless this channel is coordinator. + * @param state_id the substate id for partial state transfer. If null entire state will be transferred. + * @param timeout the timeout for state transfer. + * + * @exception ChannelException The protocol stack cannot be started + * @exception ChannelException Connecting to cluster was not successful + * @exception ChannelClosedException The channel is closed and therefore cannot be used any longer. + * A new channel has to be created first. + * @exception StateTransferException State transfer was not successful + * + */ + public synchronized void connect(String cluster_name, + Address target, + String state_id, + long timeout) throws ChannelException { + + if(connected) { + if(log.isTraceEnabled()) log.trace("already connected to " + cluster_name); + return; + } + + startStack(cluster_name); + + boolean stateTransferOk=false; + boolean joinSuccessful=false; + boolean canFetchState=false; + // only connect if we are not a unicast channel + if(cluster_name != null) { + + try { + Event connect_event=new Event(Event.CONNECT_WITH_STATE_TRANSFER, cluster_name); + Object res=downcall(connect_event); // waits forever until + // connected (or channel is + // closed) + joinSuccessful=!(res != null && res instanceof Exception); + if(!joinSuccessful) { + stopStack(true, false); + init(); + throw new ChannelException("connect() failed", (Throwable)res); + } + + connected=true; + notifyChannelConnected(this); + canFetchState=getView() != null && getView().size() > 1; + + // if I am not the only member in cluster then + if(canFetchState) { + try { + // fetch state from target + stateTransferOk=getState(target, state_id, timeout, false); + if(!stateTransferOk) { + throw new StateTransferException(getLocalAddress() + " could not fetch state " + + state_id + + " from " + + target); + } + } + catch(Exception e) { + throw new StateTransferException(getLocalAddress() + " could not fetch state " + + state_id + + " from " + + target, e); + } + } + + } + finally { + if(flushSupported() && canFetchState) + stopFlush(); + } + } + } + + + /** + * Disconnects the channel if it is connected. If the channel is closed, + * this operation is ignored
+ * Otherwise the following actions happen in the listed order
+ *

    + *
  1. The JChannel sends a DISCONNECT event down the protocol stack
    + *
  2. Blocks until the event has returned
    + *
  3. Sends a STOP_QUEING event down the stack
    + *
  4. Stops the protocol stack by calling ProtocolStack.stop()
    + *
  5. Notifies the listener, if the listener is available
    + *
+ */ + public synchronized void disconnect() { + if(closed) return; + + if(connected) { + + if(cluster_name != null) { + // Send down a DISCONNECT event, which travels down to the GMS, where a response is returned + Event disconnect_event=new Event(Event.DISCONNECT, local_addr); + down(disconnect_event); // DISCONNECT is handled by each layer + } + connected=false; + stopStack(true, false); + notifyChannelDisconnected(this); + init(); // sets local_addr=null; changed March 18 2003 (bela) -- prevented successful rejoining + } + } + + + /** + * Destroys the channel. + * After this method has been called, the channel us unusable.
+ * This operation will disconnect the channel and close the channel receive queue immediately
+ */ + public synchronized void close() { + _close(true, true); // by default disconnect before closing channel and close mq + } + + + /** Shuts down the channel without disconnecting */ + public synchronized void shutdown() { + down(new Event(Event.SHUTDOWN)); + _close(false, true); // by default disconnect before closing channel and close mq + } + + /** + * Opens the channel. Note that the channel is only open, but not connected. + * This does the following actions: + *
    + *
  1. Resets the receiver queue by calling Queue.reset + *
  2. Sets up the protocol stack by calling ProtocolStack.setup + *
  3. Sets the closed flag to false + *
+ */ + public synchronized void open() throws ChannelException { + if(!closed) + throw new ChannelException("channel is already open"); + + try { + mq.reset(); + + // new stack is created on open() - bela June 12 2003 + prot_stack=new ProtocolStack(this, props); + prot_stack.setup(); + closed=false; + } + catch(Exception e) { + throw new ChannelException("failed to open channel" , e); + } + } + + /** + * returns true if the Open operation has been called successfully + */ + public boolean isOpen() { + return !closed; + } + + + /** + * returns true if the Connect operation has been called successfully + */ + public boolean isConnected() { + return connected; + } + + public int getNumMessages() { + return mq != null? mq.size() : -1; + } + + + public String dumpQueue() { + return Util.dumpQueue(mq); + } + + /** + * Returns a map of statistics of the various protocols and of the channel itself. + * @return Map. A map where the keys are the protocols ("channel" pseudo key is + * used for the channel itself") and the values are property maps. + */ + public Map dumpStats() { + Map retval=prot_stack.dumpStats(); + if(retval != null) { + Map tmp=dumpChannelStats(); + if(tmp != null) + retval.put("channel", tmp); + } + return retval; + } + + public Map dumpStats(String protocol_name) { + return prot_stack.dumpStats(protocol_name); + } + + protected Map dumpChannelStats() { + Map retval=new HashMap(); + retval.put("sent_msgs", new Long(sent_msgs)); + retval.put("sent_bytes", new Long(sent_bytes)); + retval.put("received_msgs", new Long(received_msgs)); + retval.put("received_bytes", new Long(received_bytes)); + return retval; + } + + + /** + * Sends a message through the protocol stack. + * Implements the Transport interface. + * + * @param msg the message to be sent through the protocol stack, + * the destination of the message is specified inside the message itself + * @exception ChannelNotConnectedException + * @exception ChannelClosedException + */ + public void send(Message msg) throws ChannelNotConnectedException, ChannelClosedException { + checkClosedOrNotConnected(); + if(msg == null) + throw new NullPointerException("msg is null"); + if(stats) { + sent_msgs++; + sent_bytes+=msg.getLength(); + } + + down(new Event(Event.MSG, msg)); + } + + + /** + * creates a new message with the destination address, and the source address + * and the object as the message value + * @param dst - the destination address of the message, null for all members + * @param src - the source address of the message + * @param obj - the value of the message + * @exception ChannelNotConnectedException + * @exception ChannelClosedException + * @see JChannel#send + */ + public void send(Address dst, Address src, Serializable obj) throws ChannelNotConnectedException, ChannelClosedException { + send(new Message(dst, src, obj)); + } + + + /** + * Blocking receive method. + * This method returns the object that was first received by this JChannel and that has not been + * received before. After the object is received, it is removed from the receive queue.
+ * If you only want to inspect the object received without removing it from the queue call + * JChannel.peek
+ * If no messages are in the receive queue, this method blocks until a message is added or the operation times out
+ * By specifying a timeout of 0, the operation blocks forever, or until a message has been received. + * @param timeout the number of milliseconds to wait if the receive queue is empty. 0 means wait forever + * @exception TimeoutException if a timeout occured prior to a new message was received + * @exception ChannelNotConnectedException + * @exception ChannelClosedException + * @see JChannel#peek + * @deprecated Use a {@link Receiver} instead + */ + public Object receive(long timeout) throws ChannelNotConnectedException, ChannelClosedException, TimeoutException { + + checkClosedOrNotConnected(); + + try { + Event evt=(timeout <= 0)? (Event)mq.remove() : (Event)mq.remove(timeout); + Object retval=getEvent(evt); + evt=null; + return retval; + } + catch(QueueClosedException queue_closed) { + throw new ChannelClosedException(); + } + catch(TimeoutException t) { + throw t; + } + catch(Exception e) { + if(log.isErrorEnabled()) log.error("exception: " + e); + return null; + } + } + + + /** + * Just peeks at the next message, view or block. Does not install + * new view if view is received
+ * Does the same thing as JChannel.receive but doesn't remove the object from the + * receiver queue + */ + public Object peek(long timeout) throws ChannelNotConnectedException, ChannelClosedException, TimeoutException { + + checkClosedOrNotConnected(); + + try { + Event evt=(timeout <= 0)? (Event)mq.peek() : (Event)mq.peek(timeout); + Object retval=getEvent(evt); + evt=null; + return retval; + } + catch(QueueClosedException queue_closed) { + if(log.isErrorEnabled()) log.error("exception: " + queue_closed); + return null; + } + catch(TimeoutException t) { + return null; + } + catch(Exception e) { + if(log.isErrorEnabled()) log.error("exception: " + e); + return null; + } + } + + + + + /** + * Returns the current view. + *
+ * If the channel is not connected or if it is closed it will return null. + *
+ * @return returns the current group view, or null if the channel is closed or disconnected + */ + public View getView() { + return closed || !connected ? null : my_view; + } + + + /** + * returns the local address of the channel + * returns null if the channel is closed + */ + public Address getLocalAddress() { + return closed ? null : local_addr; + } + + public String getLocalAddressAsString() { + return local_addr != null? local_addr.toString() : "n/a"; + } + + + /** + * returns the name of the channel + * if the channel is not connected or if it is closed it will return null + * @deprecated Use {@link #getClusterName()} instead + */ + public String getChannelName() { + return closed ? null : !connected ? null : cluster_name; + } + + public String getClusterName() { + return closed ? null : !connected ? null : cluster_name; + } + + + /** + * Sets a channel option. The options can be one of the following: + *
    + *
  • Channel.BLOCK + *
  • Channel.LOCAL + *
  • Channel.AUTO_RECONNECT + *
  • Channel.AUTO_GETSTATE + *
+ *

+ * There are certain dependencies between the options that you can set, + * I will try to describe them here. + *

+ * Option: Channel.BLOCK
+ * Value: java.lang.Boolean
+ * Result: set to true will set setOpt(VIEW, true) and the JChannel will receive BLOCKS and VIEW events
+ *
+ * Option: LOCAL
+ * Value: java.lang.Boolean
+ * Result: set to true the JChannel will receive messages that it self sent out.
+ *
+ * Option: AUTO_RECONNECT
+ * Value: java.lang.Boolean
+ * Result: set to true and the JChannel will try to reconnect when it is being closed
+ *
+ * Option: AUTO_GETSTATE
+ * Value: java.lang.Boolean
+ * Result: set to true, the AUTO_RECONNECT will be set to true and the JChannel will try to get the state after a close and reconnect happens
+ *
+ * + * @param option the parameter option Channel.VIEW, Channel.SUSPECT, etc + * @param value the value to set for this option + * + */ + public void setOpt(int option, Object value) { + if(closed) { + if(log.isWarnEnabled()) log.warn("channel is closed; option not set !"); + return; + } + + switch(option) { + case VIEW: + if(log.isWarnEnabled()) + log.warn("option VIEW has been deprecated (it is always true now); this option is ignored"); + break; + case SUSPECT: + if(log.isWarnEnabled()) + log.warn("option SUSPECT has been deprecated (it is always true now); this option is ignored"); + break; + case BLOCK: + if(value instanceof Boolean) + receive_blocks=((Boolean)value).booleanValue(); + else + if(log.isErrorEnabled()) log.error("option " + Channel.option2String(option) + + " (" + value + "): value has to be Boolean"); + break; + + case GET_STATE_EVENTS: + if(log.isWarnEnabled()) + log.warn("option GET_STATE_EVENTS has been deprecated (it is always true now); this option is ignored"); + break; + + case LOCAL: + if(value instanceof Boolean) + receive_local_msgs=((Boolean)value).booleanValue(); + else + if(log.isErrorEnabled()) log.error("option " + Channel.option2String(option) + + " (" + value + "): value has to be Boolean"); + break; + + case AUTO_RECONNECT: + if(value instanceof Boolean) + auto_reconnect=((Boolean)value).booleanValue(); + else + if(log.isErrorEnabled()) log.error("option " + Channel.option2String(option) + + " (" + value + "): value has to be Boolean"); + break; + + case AUTO_GETSTATE: + if(value instanceof Boolean) { + auto_getstate=((Boolean)value).booleanValue(); + if(auto_getstate) + auto_reconnect=true; + } + else + if(log.isErrorEnabled()) log.error("option " + Channel.option2String(option) + + " (" + value + "): value has to be Boolean"); + break; + + default: + if(log.isErrorEnabled()) log.error("option " + Channel.option2String(option) + " not known"); + break; + } + } + + + /** + * returns the value of an option. + * @param option the option you want to see the value for + * @return the object value, in most cases java.lang.Boolean + * @see JChannel#setOpt + */ + public Object getOpt(int option) { + switch(option) { + case VIEW: + return Boolean.TRUE; + case BLOCK: + return receive_blocks ? Boolean.TRUE : Boolean.FALSE; + case SUSPECT: + return Boolean.TRUE; + case AUTO_RECONNECT: + return auto_reconnect ? Boolean.TRUE : Boolean.FALSE; + case AUTO_GETSTATE: + return auto_getstate ? Boolean.TRUE : Boolean.FALSE; + case GET_STATE_EVENTS: + return Boolean.TRUE; + case LOCAL: + return receive_local_msgs ? Boolean.TRUE : Boolean.FALSE; + default: + if(log.isErrorEnabled()) log.error("option " + Channel.option2String(option) + " not known"); + return null; + } + } + + + /** + * Called to acknowledge a block() (callback in MembershipListener or + * BlockEvent received from call to receive()). + * After sending blockOk(), no messages should be sent until a new view has been received. + * Calling this method on a closed channel has no effect. + */ + public void blockOk() { + + } + + + /** + * Retrieves a full state from the target member. + *

+ * + * State transfer is initiated by invoking getState on this channel, state + * receiver, and sending a GET_STATE message to a target member - state + * provider. State provider passes GET_STATE message to application that is + * using the state provider channel which in turn provides an application + * state to a state receiver. Upon successful installation of a state at + * state receiver this method returns true. + * + * + * @param target + * State provider. If null, coordinator is used + * @param state_id + * The ID of the substate. If null, the entire state will be + * transferred + * @param timeout + * the number of milliseconds to wait for the operation to + * complete successfully. 0 waits until the state has been + * received + * + * @see ExtendedMessageListener#getState(OutputStream) + * @see ExtendedMessageListener#setState(InputStream) + * @see MessageListener#getState() + * @see MessageListener#setState(byte[]) + * + * + * @return true if state transfer was successful, false otherwise + * @throws ChannelNotConnectedException + * if channel was not connected at the time state retrieval + * was initiated + * @throws ChannelClosedException + * if channel was closed at the time state retrieval was + * initiated + * @throws IllegalStateException + * if one of state transfer protocols is not present in this + * channel + * @throws IllegalStateException + * if flush is used in this channel and cluster could not be + * flushed + */ + public boolean getState(Address target, long timeout) throws ChannelNotConnectedException, ChannelClosedException { + return getState(target,null,timeout); + } + + /** + * Retrieves a substate (or partial state) indicated by state_id from the target member. + *

+ * + * State transfer is initiated by invoking getState on this channel, state + * receiver, and sending a GET_STATE message to a target member - state + * provider. State provider passes GET_STATE message to application that is + * using the state provider channel which in turn provides an application + * state to a state receiver. Upon successful installation of a state at + * state receiver this method returns true. + * + * + * @param target + * State provider. If null, coordinator is used + * @param state_id + * The ID of the substate. If null, the entire state will be + * transferred + * @param timeout + * the number of milliseconds to wait for the operation to + * complete successfully. 0 waits until the state has been + * received + * + * @see ExtendedMessageListener#getState(OutputStream) + * @see ExtendedMessageListener#setState(InputStream) + * @see MessageListener#getState() + * @see MessageListener#setState(byte[]) + * + * + * @return true if state transfer was successful, false otherwise + * @throws ChannelNotConnectedException + * if channel was not connected at the time state retrieval + * was initiated + * @throws ChannelClosedException + * if channel was closed at the time state retrieval was + * initiated + * @throws IllegalStateException + * if one of state transfer protocols is not present in this + * channel + * @throws IllegalStateException + * if flush is used in this channel and cluster could not be + * flushed + */ + public boolean getState(Address target, String state_id, long timeout) throws ChannelNotConnectedException, ChannelClosedException { + return getState(target, state_id, timeout, true); + } + + /** + * Retrieves a substate (or partial state) indicated by state_id from the target member. + *

+ * + * State transfer is initiated by invoking getState on this channel, state + * receiver, and sending a GET_STATE message to a target member - state + * provider. State provider passes GET_STATE message to application that is + * using the state provider channel which in turn provides an application + * state to a state receiver. Upon successful installation of a state at + * state receiver this method returns true. + * + * + * @param target + * State provider. If null, coordinator is used + * @param state_id + * The ID of the substate. If null, the entire state will be + * transferred + * @param timeout + * the number of milliseconds to wait for the operation to + * complete successfully. 0 waits until the state has been + * received + * @param useFlushIfPresent + * whether channel should be flushed prior to state retrieval + * + * @see ExtendedMessageListener#getState(OutputStream) + * @see ExtendedMessageListener#setState(InputStream) + * @see MessageListener#getState() + * @see MessageListener#setState(byte[]) + * + * + * @return true if state transfer was successful, false otherwise + * @throws ChannelNotConnectedException + * if channel was not connected at the time state retrieval + * was initiated + * @throws ChannelClosedException + * if channel was closed at the time state retrieval was + * initiated + * @throws IllegalStateException + * if one of state transfer protocols is not present in this + * channel + * @throws IllegalStateException + * if flush is used in this channel and cluster could not be + * flushed + */ + public boolean getState(Address target, String state_id, long timeout, + boolean useFlushIfPresent) throws ChannelNotConnectedException, + ChannelClosedException { + + Callable flusher = new Callable() { + public Boolean call() throws Exception { + return Util.startFlush(JChannel.this); + } + }; + return getState(target, state_id, timeout, useFlushIfPresent?flusher:null); + } + + /** + * Retrieves a substate (or partial state) indicated by state_id from the target member. + *

+ * + * State transfer is initiated by invoking getState on this channel, state + * receiver, and sending a GET_STATE message to a target member - state + * provider. State provider passes GET_STATE message to application that is + * using the state provider channel which in turn provides an application + * state to a state receiver. Upon successful installation of a state at + * state receiver this method returns true. + * + * + * @param target + * State provider. If null, coordinator is used + * @param state_id + * The ID of the substate. If null, the entire state will be + * transferred + * @param timeout + * the number of milliseconds to wait for the operation to + * complete successfully. 0 waits until the state has been + * received + * @param flushInvoker + * algorithm invoking flush + * + * @see ExtendedMessageListener#getState(OutputStream) + * @see ExtendedMessageListener#setState(InputStream) + * @see MessageListener#getState() + * @see MessageListener#setState(byte[]) + * + * + * @return true if state transfer was successful, false otherwise + * @throws ChannelNotConnectedException + * if channel was not connected at the time state retrieval + * was initiated + * @throws ChannelClosedException + * if channel was closed at the time state retrieval was + * initiated + * @throws IllegalStateException + * if one of state transfer protocols is not present in this + * channel + * @throws IllegalStateException + * if flush is used in this channel and cluster could not be + * flushed + */ + protected boolean getState(Address target, String state_id, long timeout,Callable flushInvoker) throws ChannelNotConnectedException, ChannelClosedException { + checkClosedOrNotConnected(); + if(!state_transfer_supported) { + throw new IllegalStateException("fetching state will fail as state transfer is not supported. " + + "Add one of the STATE_TRANSFER protocols to your protocol configuration"); + } + + if(target == null) + target=determineCoordinator(); + if(target != null && local_addr != null && target.equals(local_addr)) { + if(log.isTraceEnabled()) + log.trace("cannot get state from myself (" + target + "): probably the first member"); + return false; + } + + boolean initiateFlush = flushSupported() && flushInvoker!=null; + + if (initiateFlush) { + boolean successfulFlush = false; + try { + successfulFlush = flushInvoker.call(); + } + catch (Exception e) { + successfulFlush = false; + // http://jira.jboss.com/jira/browse/JGRP-759 + } + finally { + if (!successfulFlush) { + throw new IllegalStateException("Node "+ local_addr+ " could not flush the cluster for state retrieval"); + } + } + } + + state_promise.reset(); + StateTransferInfo state_info=new StateTransferInfo(target, state_id, timeout); + down(new Event(Event.GET_STATE, state_info)); + Boolean b=state_promise.getResult(state_info.timeout); + + if(initiateFlush) + stopFlush(); + + boolean state_transfer_successfull = b != null && b.booleanValue(); + if(!state_transfer_successfull) + down(new Event(Event.RESUME_STABLE)); + return state_transfer_successfull; + } + + + /** + * Retrieves the current group state. Sends GET_STATE event down to STATE_TRANSFER layer. + * Blocks until STATE_TRANSFER sends up a GET_STATE_OK event or until timeout + * milliseconds have elapsed. The argument of GET_STATE_OK should be a vector of objects. + * @param targets - the target members to receive the state from ( an Address list ) + * @param timeout - the number of milliseconds to wait for the operation to complete successfully + * @return true of the state was received, false if the operation timed out + * @deprecated Not really needed - we always want to get the state from a single member, + * use {@link #getState(org.jgroups.Address, long)} instead + */ + public boolean getAllStates(Vector targets, long timeout) throws ChannelNotConnectedException, ChannelClosedException { + throw new UnsupportedOperationException("use getState() instead"); + } + + + /** + * Called by the application is response to receiving a getState() object when + * calling receive(). + * When the application receives a getState() message on the receive() method, + * it should call returnState() to reply with the state of the application + * @param state The state of the application as a byte buffer + * (to send over the network). + */ + public void returnState(byte[] state) { + try { + StateTransferInfo state_info=new StateTransferInfo(null, null, 0L, state); + applstate_exchanger.exchange(state_info); + } + catch(InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + /** + * Returns a substate as indicated by state_id + * @param state + * @param state_id + */ + public void returnState(byte[] state, String state_id) { + try { + StateTransferInfo state_info=new StateTransferInfo(null, state_id, 0L, state); + applstate_exchanger.exchange(state_info); + } + catch(InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + + + + + /** + * Callback method
+ * Called by the ProtocolStack when a message is received. + * It will be added to the message queue from which subsequent + * Receives will dequeue it. + * @param evt the event carrying the message from the protocol stack + */ + public Object up(Event evt) { + int type=evt.getType(); + Message msg; + + + switch(type) { + + case Event.MSG: + msg=(Message)evt.getArg(); + if(stats) { + received_msgs++; + received_bytes+=msg.getLength(); + } + + if(!receive_local_msgs) { // discard local messages (sent by myself to me) + if(local_addr != null && msg.getSrc() != null) + if(local_addr.equals(msg.getSrc())) + return null; + } + break; + + case Event.VIEW_CHANGE: + View tmp=(View)evt.getArg(); + if(tmp instanceof MergeView) + my_view=new View(tmp.getVid(), tmp.getMembers()); + else + my_view=tmp; + + /* + * Bela&Vladimir Oct 27th,2006 (JGroups 2.4)- we need to switch to + * connected=true because client can invoke channel.getView() in + * viewAccepted() callback invoked on this thread + * (see Event.VIEW_CHANGE handling below) + */ + + // not good: we are only connected when we returned from connect() - bela June 22 2007 + // if(connected == false) { + // connected=true; + // } + break; + + case Event.CONFIG: + Map config=(Map)evt.getArg(); + if(config != null) { + if(config.containsKey("state_transfer")) { + state_transfer_supported=((Boolean)config.get("state_transfer")).booleanValue(); + } + if(config.containsKey("flush_supported")) { + flush_supported=((Boolean)config.get("flush_supported")).booleanValue(); + } + } + break; + + case Event.INFO: + Map m = (Map) evt.getArg(); + info.putAll(m); + break; + + case Event.GET_STATE_OK: + StateTransferInfo state_info = (StateTransferInfo) evt.getArg(); + byte[] state = state_info.state; + + try{ + if(up_handler != null){ + return up_handler.up(evt); + } + + if(state != null){ + String state_id = state_info.state_id; + if(receiver != null){ + try{ + if(receiver instanceof ExtendedReceiver && state_id != null) + ((ExtendedReceiver) receiver).setState(state_id, state); + else + receiver.setState(state); + }catch(Throwable t){ + if(log.isWarnEnabled()) + log.warn("failed calling setState() in receiver", t); + } + }else{ + try{ + mq.add(new Event(Event.STATE_RECEIVED, state_info)); + } + catch(Exception e) { + } + } + } + } + finally { + state_promise.setResult(state != null ? Boolean.TRUE : Boolean.FALSE); + } + break; + case Event.STATE_TRANSFER_INPUTSTREAM_CLOSED: + state_promise.setResult(Boolean.TRUE); + break; + + case Event.STATE_TRANSFER_INPUTSTREAM: + StateTransferInfo sti=(StateTransferInfo)evt.getArg(); + InputStream is=sti.inputStream; + //Oct 13,2006 moved to down() when Event.STATE_TRANSFER_INPUTSTREAM_CLOSED is received + //state_promise.setResult(is != null? Boolean.TRUE : Boolean.FALSE); + + if(up_handler != null) { + return up_handler.up(evt); + } + + if(is != null) { + if(receiver instanceof ExtendedReceiver) { + try { + if(sti.state_id == null) + ((ExtendedReceiver)receiver).setState(is); + else + ((ExtendedReceiver)receiver).setState(sti.state_id, is); + } + catch(Throwable t) { + if(log.isWarnEnabled()) + log.warn("failed calling setState() in receiver", t); + } + } + else if(receiver instanceof Receiver){ + if(log.isWarnEnabled()){ + log.warn("Channel has STREAMING_STATE_TRANSFER, however," + + " application does not implement ExtendedMessageListener. State is not transfered"); + Util.close(is); + } + } + else { + try { + mq.add(new Event(Event.STATE_TRANSFER_INPUTSTREAM, sti)); + } + catch(Exception e) { + } + } + } + break; + + case Event.SET_LOCAL_ADDRESS: + local_addr_promise.setResult((Address)evt.getArg()); + break; + + case Event.EXIT: + handleExit(evt); + return null; // no need to pass event up; already done in handleExit() + + default: + break; + } + + + // If UpHandler is installed, pass all events to it and return (UpHandler is e.g. a building block) + if(up_handler != null) { + Object ret=up_handler.up(evt); + + if(type == Event.UNBLOCK){ + flush_unblock_promise.setResult(Boolean.TRUE); + } + return ret; + } + + switch(type) { + case Event.MSG: + if(receiver != null) { + try { + receiver.receive((Message)evt.getArg()); + } + catch(Throwable t) { + if(log.isWarnEnabled()) + log.warn("failed calling receive() in receiver", t); + } + return null; + } + break; + case Event.VIEW_CHANGE: + if(receiver != null) { + try { + receiver.viewAccepted((View)evt.getArg()); + } + catch(Throwable t) { + if(log.isWarnEnabled()) + log.warn("failed calling viewAccepted() in receiver", t); + } + return null; + } + break; + case Event.SUSPECT: + if(receiver != null) { + try { + receiver.suspect((Address)evt.getArg()); + } + catch(Throwable t) { + if(log.isWarnEnabled()) + log.warn("failed calling suspect() in receiver", t); + } + return null; + } + break; + case Event.GET_APPLSTATE: + if(receiver != null) { + StateTransferInfo state_info=(StateTransferInfo)evt.getArg(); + byte[] tmp_state=null; + String state_id=state_info.state_id; + try { + if(receiver instanceof ExtendedReceiver && state_id!=null) { + tmp_state=((ExtendedReceiver)receiver).getState(state_id); + } + else { + tmp_state=receiver.getState(); + } + } + catch(Throwable t) { + if(log.isWarnEnabled()) + log.warn("failed calling getState() in receiver", t); + } + return new StateTransferInfo(null, state_id, 0L, tmp_state); + } + break; + case Event.STATE_TRANSFER_OUTPUTSTREAM: + StateTransferInfo sti=(StateTransferInfo)evt.getArg(); + OutputStream os=sti.outputStream; + if(receiver instanceof ExtendedReceiver) { + if(os != null) { + try { + if(sti.state_id == null) + ((ExtendedReceiver)receiver).getState(os); + else + ((ExtendedReceiver)receiver).getState(sti.state_id, os); + } + catch(Throwable t) { + if(log.isWarnEnabled()) + log.warn("failed calling getState() in receiver", t); + } + } + } + else if(receiver instanceof Receiver){ + if(log.isWarnEnabled()){ + log.warn("Channel has STREAMING_STATE_TRANSFER, however," + + " application does not implement ExtendedMessageListener. State is not transfered"); + Util.close(os); + } + } + break; + + case Event.BLOCK: + if(!receive_blocks) { // discard if client has not set 'receiving blocks' to 'on' + return Boolean.TRUE; + } + + if(receiver != null) { + try { + receiver.block(); + } + catch(Throwable t) { + if(log.isErrorEnabled()) + log.error("failed calling block() in receiver", t); + } + return Boolean.TRUE; + } + break; + case Event.UNBLOCK: + //invoke receiver if block receiving is on + if(receive_blocks && receiver instanceof ExtendedReceiver) { + try { + ((ExtendedReceiver)receiver).unblock(); + } + catch(Throwable t) { + if(log.isErrorEnabled()) + log.error("failed calling unblock() in receiver", t); + } + } + //flip promise + flush_unblock_promise.setResult(Boolean.TRUE); + return null; + default: + break; + } + + if(type == Event.MSG || type == Event.VIEW_CHANGE || type == Event.SUSPECT || + type == Event.GET_APPLSTATE || type== Event.STATE_TRANSFER_OUTPUTSTREAM + || type == Event.BLOCK || type == Event.UNBLOCK) { + try { + mq.add(evt); + } + catch(QueueClosedException queue_closed) { + ; // ignore + } + catch(Exception e) { + if(log.isWarnEnabled()) log.warn("exception adding event " + evt + " to message queue", e); + } + } + + if(type == Event.GET_APPLSTATE) { + try { + return applstate_exchanger.exchange(null); + } + catch(InterruptedException e) { + Thread.currentThread().interrupt(); + return null; + } + } + return null; + } + + + /** + * Sends a message through the protocol stack if the stack is available + * @param evt the message to send down, encapsulated in an event + */ + public void down(Event evt) { + if(evt == null) return; + + switch(evt.getType()) { + case Event.CONFIG: + try { + Map m=(Map)evt.getArg(); + if(m != null) { + additional_data.putAll(m); + if(m.containsKey("additional_data")) { + byte[] tmp=(byte[])m.get("additional_data"); + if(local_addr instanceof IpAddress) + ((IpAddress)local_addr).setAdditionalData(tmp); + } + } + } + catch(Throwable t) { + if(log.isErrorEnabled()) log.error("CONFIG event did not contain a hashmap: " + t); + } + break; + } + + prot_stack.down(evt); + } + + + public Object downcall(Event evt) { + if(evt == null) return null; + + switch(evt.getType()) { + case Event.CONFIG: + try { + Map m=(Map)evt.getArg(); + if(m != null) { + additional_data.putAll(m); + if(m.containsKey("additional_data")) { + byte[] tmp=(byte[])m.get("additional_data"); + if(local_addr instanceof IpAddress) + ((IpAddress)local_addr).setAdditionalData(tmp); + } + } + } + catch(Throwable t) { + if(log.isErrorEnabled()) log.error("CONFIG event did not contain a hashmap: " + t); + } + break; + } + + return prot_stack.down(evt); + } + + + + public String toString(boolean details) { + StringBuilder sb=new StringBuilder(); + sb.append("local_addr=").append(local_addr).append('\n'); + sb.append("cluster_name=").append(cluster_name).append('\n'); + sb.append("my_view=").append(my_view).append('\n'); + sb.append("connected=").append(connected).append('\n'); + sb.append("closed=").append(closed).append('\n'); + if(mq != null) + sb.append("incoming queue size=").append(mq.size()).append('\n'); + if(details) { + sb.append("receive_blocks=").append(receive_blocks).append('\n'); + sb.append("receive_local_msgs=").append(receive_local_msgs).append('\n'); + sb.append("auto_reconnect=").append(auto_reconnect).append('\n'); + sb.append("auto_getstate=").append(auto_getstate).append('\n'); + sb.append("state_transfer_supported=").append(state_transfer_supported).append('\n'); + sb.append("props=").append(props).append('\n'); + } + + return sb.toString(); + } + + + /* ----------------------------------- Private Methods ------------------------------------- */ + + + protected final void init(ProtocolStackConfigurator configurator) throws ChannelException { + if(log.isInfoEnabled()) + log.info("JGroups version: " + Version.description); + // ConfiguratorFactory.substituteVariables(configurator); // replace vars with system props + props=configurator.getProtocolStackString(); + props=Util.substituteVariable(props); + prot_stack=new ProtocolStack(this, props); + try { + prot_stack.setup(); // Setup protocol stack (creates protocol, calls init() on them) + } + catch(Throwable e) { + throw new ChannelException("unable to setup the protocol stack: " + e.getMessage(), e); + } + } + + + /** + * Initializes all variables. Used after close() or disconnect(), + * to be ready for new connect() + */ + private void init() { + local_addr=null; + cluster_name=null; + my_view=null; + + // changed by Bela Sept 25 2003 + //if(mq != null && mq.closed()) + // mq.reset(); + connected=false; + } + + + private void startStack(String cluster_name) throws ChannelException { + /*make sure the channel is not closed*/ + checkClosed(); + + /*make sure we have a valid channel name*/ + if(cluster_name == null) { + if(log.isDebugEnabled()) log.debug("cluster_name is null, assuming unicast channel"); + } + else + this.cluster_name=cluster_name; + + try { + prot_stack.startStack(cluster_name); // calls start() in all protocols, from top to bottom + } + catch(Throwable e) { + throw new ChannelException("failed to start protocol stack", e); + } + + String tmp=Util.getProperty(new String[]{Global.CHANNEL_LOCAL_ADDR_TIMEOUT, "local_addr.timeout"}, + null, null, false, "30000"); + LOCAL_ADDR_TIMEOUT=Long.parseLong(tmp); + + /* Wait LOCAL_ADDR_TIMEOUT milliseconds for local_addr to have a non-null value (set by SET_LOCAL_ADDRESS) */ + local_addr=local_addr_promise.getResult(LOCAL_ADDR_TIMEOUT); + if(local_addr == null) { + log.fatal("local_addr is null; cannot connect"); + throw new ChannelException("local_addr is null"); + } + + /*create a temporary view, assume this channel is the only member and is the coordinator*/ + Vector

t=new Vector
(1); + t.addElement(local_addr); + my_view=new View(local_addr, 0, t); // create a dummy view + + TP transport=prot_stack.getTransport(); + transport.registerProbeHandler(probe_handler); + } + + + + /** + * health check
+ * throws a ChannelClosed exception if the channel is closed + */ + protected void checkClosed() throws ChannelClosedException { + if(closed) + throw new ChannelClosedException(); + } + + + protected void checkClosedOrNotConnected() throws ChannelNotConnectedException, ChannelClosedException { + if(closed) + throw new ChannelClosedException(); + if(!connected) + throw new ChannelNotConnectedException(); + } + + + /** + * returns the value of the event
+ * These objects will be returned
+ *
+     * Event Type    - Return Type
+     * Event.MSG           - returns a Message object
+     * Event.VIEW_CHANGE   - returns a View object
+     * Event.SUSPECT       - returns a SuspectEvent object
+     * Event.BLOCK         - returns a new BlockEvent object
+     * Event.GET_APPLSTATE - returns a GetStateEvent object
+     * Event.STATE_RECEIVED- returns a SetStateEvent object
+     * Event.Exit          - returns an ExitEvent object
+     * All other           - return the actual Event object
+     * 
+ * @param evt - the event of which you want to extract the value + * @return the event value if it matches the select list, + * returns null if the event is null + * returns the event itself if a match (See above) can not be made of the event type + */ + static Object getEvent(Event evt) { + if(evt == null) + return null; // correct ? + + switch(evt.getType()) { + case Event.MSG: + return evt.getArg(); + case Event.VIEW_CHANGE: + return evt.getArg(); + case Event.SUSPECT: + return new SuspectEvent(evt.getArg()); + case Event.BLOCK: + return new BlockEvent(); + case Event.UNBLOCK: + return new UnblockEvent(); + case Event.GET_APPLSTATE: + StateTransferInfo info=(StateTransferInfo)evt.getArg(); + return new GetStateEvent(info.target, info.state_id); + case Event.STATE_RECEIVED: + info=(StateTransferInfo)evt.getArg(); + return new SetStateEvent(info.state, info.state_id); + case Event.STATE_TRANSFER_OUTPUTSTREAM: + info = (StateTransferInfo)evt.getArg(); + return new StreamingGetStateEvent(info.outputStream,info.state_id); + case Event.STATE_TRANSFER_INPUTSTREAM: + info = (StateTransferInfo)evt.getArg(); + return new StreamingSetStateEvent(info.inputStream,info.state_id); + case Event.EXIT: + return new ExitEvent(); + default: + return evt; + } + } + + /** + * Disconnects and closes the channel. + * This method does the following things + *
    + *
  1. Calls this.disconnect if the disconnect parameter is true + *
  2. Calls Queue.close on mq if the close_mq parameter is true + *
  3. Calls ProtocolStack.stop on the protocol stack + *
  4. Calls ProtocolStack.destroy on the protocol stack + *
  5. Sets the channel closed and channel connected flags to true and false + *
  6. Notifies any channel listener of the channel close operation + *
+ */ + protected void _close(boolean disconnect, boolean close_mq) { + if(closed) + return; + + if(disconnect) + disconnect(); // leave group if connected + + if(close_mq) + closeMessageQueue(false); + + stopStack(true, true); + closed=true; + connected=false; + notifyChannelClosed(this); + init(); // sets local_addr=null; changed March 18 2003 (bela) -- prevented successful rejoining + } + + protected void stopStack(boolean disconnect, boolean destroy) { + if(prot_stack != null) { + try { + if(disconnect) + prot_stack.stopStack(cluster_name); + + if(destroy) + prot_stack.destroy(); + } + catch(Exception e) { + if(log.isErrorEnabled()) + log.error("failed destroying the protocol stack", e); + } + + TP transport=prot_stack.getTransport(); + if(transport != null) + transport.unregisterProbeHandler(probe_handler); + } + } + + + public final void closeMessageQueue(boolean flush_entries) { + if(mq != null) + mq.close(flush_entries); + } + + + /** + * Creates a separate thread to close the protocol stack. + * This is needed because the thread that called JChannel.up() with the EXIT event would + * hang waiting for up() to return, while up() actually tries to kill that very thread. + * This way, we return immediately and allow the thread to terminate. + */ + private synchronized void handleExit(Event evt) { + notifyChannelShunned(); + if(!auto_reconnect) + return; + + if(closer != null && !closer.isAlive()) + closer=null; + if(closer == null) { + if(log.isDebugEnabled()) + log.debug("received an EXIT event, will leave the channel"); + closer=new CloserThread(evt); + closer.start(); + } + } + + public boolean flushSupported() { + return flush_supported; + } + + /** + * Will perform a flush of the system, ie. all pending messages are flushed out of the + * system and all members ack their reception. After this call returns, no member will + * be sending any messages until {@link #stopFlush()} is called. + *

+ * In case of flush collisions, random sleep time backoff algorithm is employed and + * flush is reattempted for numberOfAttempts. Therefore this method is guaranteed + * to return after timeout x numberOfAttempts miliseconds. + * + * @param automatic_resume Call {@link #stopFlush()} after the flush + * @return true if FLUSH completed within the timeout + */ + public boolean startFlush(boolean automatic_resume) { + if(!flushSupported()) { + throw new IllegalStateException("Flush is not supported, add pbcast.FLUSH protocol to your configuration"); + } + boolean successfulFlush = (Boolean) downcall(new Event(Event.SUSPEND)); + + if(automatic_resume) + stopFlush(); + + return successfulFlush; + } + + /** + * Performs a partial flush in a cluster for flush participants. + *

+ * All pending messages are flushed out only for flush participants. + * Remaining members in a cluster are not included in flush. + * Flush participants should be a proper subset of a current view. + * + *

+ * In case of flush collisions, random sleep time backoff algorithm is employed and + * flush is reattempted for numberOfAttempts. Therefore this method is guaranteed + * to return after timeout x numberOfAttempts miliseconds. + * + * @param automatic_resume Call {@link #stopFlush()} after the flush + * @return true if FLUSH completed within the timeout + */ + public boolean startFlush(List

flushParticipants,boolean automatic_resume) { + boolean successfulFlush = false; + if(!flushSupported()){ + throw new IllegalStateException("Flush is not supported, add pbcast.FLUSH protocol to your configuration"); + } + View v = getView(); + if(v != null && v.getMembers().containsAll(flushParticipants)){ + successfulFlush = (Boolean) downcall(new Event(Event.SUSPEND, flushParticipants)); + }else{ + throw new IllegalArgumentException("Current view " + v + + " does not contain all flush participants " + + flushParticipants); + } + + if(automatic_resume) + stopFlush(flushParticipants); + + return successfulFlush; + } + + /** + * Will perform a flush of the system, ie. all pending messages are flushed out of the + * system and all members ack their reception. After this call returns, no member will + * be sending any messages until {@link #stopFlush()} is called. + *

+ * In case of flush collisions, random sleep time backoff algorithm is employed and + * flush is reattempted for numberOfAttempts. Therefore this method is guaranteed + * to return after timeout x numberOfAttempts miliseconds. + * @param timeout + * @param automatic_resume Call {@link #stopFlush()} after the flush + * @return true if FLUSH completed within the timeout + */ + public boolean startFlush(long timeout, boolean automatic_resume) { + return startFlush(automatic_resume); + } + + public void stopFlush() { + if(!flushSupported()) { + throw new IllegalStateException("Flush is not supported, add pbcast.FLUSH protocol to your configuration"); + } + flush_unblock_promise.reset(); + down(new Event(Event.RESUME)); + + //do not return until UNBLOCK event is received + try { + flush_unblock_promise.getResultWithTimeout(FLUSH_UNBLOCK_TIMEOUT); + } + catch(TimeoutException te) { + log.warn("Timeout waiting for UNBLOCK event at " + getLocalAddress()); + } + } + + public void stopFlush(List

flushParticipants) { + if(!flushSupported()) { + throw new IllegalStateException("Flush is not supported, add pbcast.FLUSH protocol to your configuration"); + } + flush_unblock_promise.reset(); + down(new Event(Event.RESUME, flushParticipants)); + + // do not return until UNBLOCK event is received + try { + flush_unblock_promise.getResultWithTimeout(FLUSH_UNBLOCK_TIMEOUT); + } + catch(TimeoutException te) { + log.warn("Timeout waiting for UNBLOCK event at " + getLocalAddress()); + } + } + + @Override + public Map getInfo(){ + return new HashMap(info); + } + + public void setInfo(String key, Object value) { + if(key != null) + info.put(key, value); + } + + Address determineCoordinator() { + Vector
mbrs=my_view != null? my_view.getMembers() : null; + if(mbrs == null) + return null; + if(!mbrs.isEmpty()) + return mbrs.firstElement(); + return null; + } + + private TimeScheduler getTimer() { + if(prot_stack != null) { + TP transport=prot_stack.getTransport(); + if(transport != null) { + return transport.getTimer(); + } + } + return null; + } + + /* ------------------------------- End of Private Methods ---------------------------------- */ + + class MyProbeHandler implements TP.ProbeHandler { + + public Map handleProbe(String... keys) { + HashMap map=new HashMap(2); + for(String key: keys) { + if(key.startsWith("jmx")) { + Map tmp_stats; + int index=key.indexOf("="); + if(index > -1) { + String value=key.substring(index +1); + tmp_stats=dumpStats(value); + } + else + tmp_stats=dumpStats(); + + map.put("jmx", tmp_stats != null? Util.mapToString(tmp_stats) : "null"); + continue; + } + if(key.equals("info")) { + Map tmp_info=getInfo(); + map.put("info", tmp_info != null? Util.mapToString(tmp_info) : "null"); + } + } + + map.put("version", Version.description + ", cvs=\"" + Version.cvs + "\""); + if(my_view != null && !map.containsKey("view")) + map.put("view", my_view.toString()); + map.put("local_addr", local_addr != null? local_addr.toString() : "null"); + map.put("cluster", getClusterName()); + map.put("member", getLocalAddressAsString() + " (" + getClusterName() + ")"); + return map; + } + + public String[] supportedKeys() { + return new String[]{"jmx", "info"}; + } + } + + /** + * Closes, reopens and reconnects to the cluster + */ + class CloserThread extends Thread { + final Event evt; + final Thread t=null; + + + CloserThread(Event evt) { + super(Util.getGlobalThreadGroup(), "CloserThread"); + this.evt=evt; + setDaemon(true); + } + + + public void run() { + String old_cluster_name=cluster_name; // remember because close() will null it + try { + if(log.isDebugEnabled()) + log.debug("closing the channel"); + _close(false, false); // do not disconnect before closing channel, do not close mq (yet !) + } + catch(Throwable ex1) { + if(log.isErrorEnabled()) + log.error("failed closing the channel", ex1); + } + + try { + if(up_handler != null) + up_handler.up(this.evt); + else { + if(receiver == null) + mq.add(this.evt); + } + } + catch(Throwable ex2) { + if(log.isErrorEnabled()) + log.error("failed passing up EXIT event", ex2); + } + + + if(mq != null) { + Util.sleep(500); // give the mq thread a bit of time to deliver EXIT to the application + try { mq.close(false); } catch(Throwable ex3) {} + } + + for(int i=0; i < 5; i++) { + try { + if(closed == false) + break; + if(log.isDebugEnabled()) log.debug("reconnecting to cluster " + old_cluster_name); + open(); + if(additional_data != null) { + // send previously set additional_data down the stack - other protocols (e.g. TP) use it + Map m=new HashMap(additional_data); + down(new Event(Event.CONFIG, m)); + } + } + catch(Throwable ex4) { + if(log.isErrorEnabled()) log.error("failure reopening channel: " + ex4); + Util.sleep(500); + } + } + + if(closed) { // still closed; reopening failed above + if(log.isErrorEnabled()) + log.error("failed reopening channel, terminating closer thread"); + closer=null; + return; + } + + while(!connected) { + try { + connect(old_cluster_name); + notifyChannelReconnected(local_addr); + } + catch(Throwable ex5) { + if(log.isErrorEnabled()) log.error("failure reconnecting to channel, retrying", ex5); + Util.sleep(1000); // sleep 1 sec between reconnect attempts + } + } + + if(auto_getstate && state_transfer_supported) { + if(log.isDebugEnabled()) + log.debug("fetching the state (auto_getstate=true)"); + boolean rc=false; + try { + rc=JChannel.this.getState(null, GET_STATE_DEFAULT_TIMEOUT); + if(log.isDebugEnabled()) { + if(rc) + log.debug("state was retrieved successfully"); + else + log.debug("state transfer failed"); + } + } + catch(Throwable ex6) { + if(log.isErrorEnabled()) + log.error("failed auto-fetching state", ex6); + } + + } + + } + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/JChannelFactory.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/JChannelFactory.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/JChannelFactory.java 17 Aug 2012 14:51:04 -0000 1.1 @@ -0,0 +1,545 @@ +// $Id: JChannelFactory.java,v 1.1 2012/08/17 14:51:04 marcin Exp $ + +package org.jgroups; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.conf.ConfiguratorFactory; +import org.jgroups.conf.ProtocolStackConfigurator; +import org.jgroups.conf.XmlConfigurator; +import org.jgroups.jmx.JmxConfigurator; +import org.jgroups.mux.Multiplexer; +import org.jgroups.mux.MuxChannel; +import org.jgroups.util.Util; +import org.w3c.dom.*; + +import javax.management.MBeanServer; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.*; + +/** + * JChannelFactory creates pure Java implementations of the Channel + * interface. + * See {@link JChannel} for a discussion of channel properties. + */ +public class JChannelFactory implements ChannelFactory { + private ProtocolStackConfigurator configurator; + + private Log log=LogFactory.getLog(getClass()); + + /** + * Map. Hashmap which maps stack names to JGroups + * configurations. Keys are stack names, values are plain JGroups stack + * configs. This is (re-)populated whenever a setMultiplexerConfig() method + * is called + */ + private final Map stacks = Collections.synchronizedMap(new HashMap()); + + /** + * Map, maintains mapping between stack names (e.g. "udp") and Multiplexer(es) + * + */ + private final Map channels = Collections.synchronizedMap(new HashMap()); + + + /** + * The MBeanServer to expose JMX management data with (no management data + * will be available if null) + */ + private MBeanServer server = null; + + /** To expose the channels and protocols */ + private String domain = "jgroups"; + + /** Whether or not to expose channels via JMX */ + private boolean expose_channels=true; + + /** Whether to expose the factory only, or all protocols as well */ + private boolean expose_protocols=true; + + + private final static String PROTOCOL_STACKS="protocol_stacks"; + private final static String STACK="stack"; + private static final String NAME="name"; + private static final String CONFIG="config"; + + /** + * Constructs a JChannelFactory instance that contains no + * protocol stack configuration. + */ + public JChannelFactory() { + } + + /** + * Constructs a JChannelFactory instance that utilizes the + * specified file for protocl stack configuration. + * + * @param properties a file containing a JGroups XML protocol stack + * configuration. + * + * @throws ChannelException if problems occur during the interpretation of + * the protocol stack configuration. + */ + public JChannelFactory(File properties) throws ChannelException { + configurator=ConfiguratorFactory.getStackConfigurator(properties); + } + + /** + * Constructs a JChannelFactory instance that utilizes the + * specified file for protocl stack configuration. + * + * @param properties a XML element containing a JGroups XML protocol stack + * configuration. + * + * @throws ChannelException if problems occur during the interpretation of + * the protocol stack configuration. + */ + public JChannelFactory(Element properties) throws ChannelException { + configurator=ConfiguratorFactory.getStackConfigurator(properties); + } + + /** + * Constructs a JChannelFactory instance that utilizes the + * specified file for protocl stack configuration. + * + * @param properties a URL pointing to a JGroups XML protocol stack + * configuration. + * + * @throws ChannelException if problems occur during the interpretation of + * the protocol stack configuration. + */ + public JChannelFactory(URL properties) throws ChannelException { + configurator=ConfiguratorFactory.getStackConfigurator(properties); + } + + /** + * Constructs a JChannel instance with the protocol stack + * configuration based upon the specified properties parameter. + * + * @param properties an old style property string, a string representing a + * system resource containing a JGroups XML configuration, + * a string representing a URL pointing to a JGroups XML + * XML configuration, or a string representing a file name + * that contains a JGroups XML configuration. + * + * @throws ChannelException if problems occur during the interpretation of + * the protocol stack configuration. + */ + public JChannelFactory(String properties) throws ChannelException { + configurator=ConfiguratorFactory.getStackConfigurator(properties); + } + + + public void setMultiplexerConfig(Object properties) throws Exception { + setMultiplexerConfig(properties, true); + } + + public void setMultiplexerConfig(Object properties, boolean replace) throws Exception { + InputStream input=ConfiguratorFactory.getConfigStream(properties); + if(input == null) + throw new FileNotFoundException(properties.toString()); + try { + parse(input, replace); + } + catch(Exception ex) { + throw new Exception("failed parsing " + properties, ex); + } + finally { + Util.close(input); + } + } + + public void setMultiplexerConfig(File file) throws Exception { + setMultiplexerConfig(file, true); + } + + public void setMultiplexerConfig(File file, boolean replace) throws Exception { + InputStream input=ConfiguratorFactory.getConfigStream(file); + if(input == null) + throw new FileNotFoundException(file.toString()); + try { + parse(input, replace); + } + catch(Exception ex) { + throw new Exception("failed parsing " + file.toString(), ex); + } + finally { + Util.close(input); + } + } + + public void setMultiplexerConfig(Element properties) throws Exception { + parse(properties, true); + } + + public void setMultiplexerConfig(Element properties, boolean replace) throws Exception { + parse(properties, replace); + } + + public void setMultiplexerConfig(URL url) throws Exception { + setMultiplexerConfig(url, true); + } + + public void setMultiplexerConfig(URL url, boolean replace) throws Exception { + InputStream input=ConfiguratorFactory.getConfigStream(url); + if(input == null) + throw new FileNotFoundException(url.toString()); + try { + parse(input, replace); + } + catch(Exception ex) { + throw new Exception("failed parsing " + url.toString(), ex); + } + finally { + Util.close(input); + } + } + + public void setMultiplexerConfig(String properties) throws Exception { + setMultiplexerConfig(properties, true); + } + + public void setMultiplexerConfig(String properties, boolean replace) throws Exception { + InputStream input=ConfiguratorFactory.getConfigStream(properties); + if(input == null) + throw new FileNotFoundException(properties); + try { + parse(input, replace); + } + catch(Exception ex) { + throw new Exception("failed parsing " + properties, ex); + } + finally { + Util.close(input); + } + } + + /** + * Returns the stack configuration as a string (to be fed into new JChannel()). Throws an exception + * if the stack_name is not found. One of the setMultiplexerConfig() methods had to be called beforehand + * @return The protocol stack config as a plain string + */ + public String getConfig(String stack_name) throws Exception { + String cfg=stacks.get(stack_name); + if(cfg == null) + throw new Exception("stack \"" + stack_name + "\" not found in " + stacks.keySet()); + return cfg; + } + + /** + * @return Returns all configurations + */ + public String getMultiplexerConfig() { + StringBuilder sb=new StringBuilder(); + for(Map.Entry entry: stacks.entrySet()) { + sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); + } + return sb.toString(); + } + + /** Removes all configurations */ + public void clearConfigurations() { + stacks.clear(); + } + + public boolean removeConfig(String stack_name) { + return stack_name != null && stacks.remove(stack_name) != null; + } + + public MBeanServer getServer() { + return server; + } + + public void setServer(MBeanServer server) { + this.server=server; + } + + public String getDomain() { + return domain; + } + + public void setDomain(String domain) { + this.domain=domain; + } + + public boolean isExposeChannels() { + return expose_channels; + } + + public void setExposeChannels(boolean expose_channels) { + this.expose_channels=expose_channels; + } + + public boolean isExposeProtocols() { + return expose_protocols; + } + + public void setExposeProtocols(boolean expose_protocols) { + this.expose_protocols=expose_protocols; + if (expose_protocols) + this.expose_channels=true; + } + + + /** + * Creates a JChannel implementation of the + * Channel interface. + * + * @param properties the protocol stack configuration information; a + * null value means use the default protocol + * stack configuration. + * + * @throws ChannelException if the creation of the channel failed. + * + * @deprecated JChannel's conversion to type-specific + * construction, and the subsequent deprecation of its + * JChannel(Object) constructor, necessitate the + * deprecation of this factory method as well. Type-specific + * protocol stack configuration should be specfied during + * construction of an instance of this factory. + */ + public Channel createChannel(Object properties) throws ChannelException { + return new JChannel(properties); + } + + /** + * Creates a JChannel implementation of the + * Channel interface using the protocol stack configuration + * information specfied during construction of an instance of this factory. + * + * @throws ChannelException if the creation of the channel failed. + */ + public Channel createChannel() throws ChannelException { + return new JChannel(configurator); + } + + public Channel createChannel(String stack_name) throws Exception { + String props=stack_name != null? getConfig(stack_name) : null; + return new JChannel(props); + } + + public Channel createMultiplexerChannel(String stack_name, String id) throws Exception { + return createMultiplexerChannel(stack_name, id, false, null); + } + + public Channel createMultiplexerChannel(final String stack_name, + String id, + boolean register_for_state_transfer, + String substate_id) throws Exception { + if(stack_name == null || id == null) + throw new IllegalArgumentException("stack name and service ID have to be non null"); + + if(stack_name.length()==0 || id.length() == 0) + throw new IllegalArgumentException("stack name and service ID have to non empty strings"); + + Multiplexer mux = null; + synchronized (channels) { + if (!channels.containsKey(stack_name)) { + JChannel ch = new JChannel(getConfig(stack_name)); + registerChannel(ch, stack_name); + mux = new Multiplexer(ch); + channels.put(stack_name, mux); + } else { + mux = channels.get(stack_name); + } + } + if(register_for_state_transfer) + mux.registerForStateTransfer(id, substate_id); + + Channel c = mux.createMuxChannel(id, stack_name); + c.addChannelListener(new MuxFactoryChannelListener()); + return c; + } + + /** + * Returns true if this factory has already registered MuxChannel with given + * stack_name and an id, false otherwise. + * + * @param stack_name + * name of the stack used + * @param id + * service id + * @return true if such MuxChannel exists, false otherwise + */ + public boolean hasMuxChannel(String stack_name, String id) { + Multiplexer entry = channels.get(stack_name); + if (entry != null) { + Set services = entry.getServiceIds(); + return (services != null && services.contains(id)); + } + return false; + } + + public void create() throws Exception{ + if(expose_channels) { + if(server == null) + server=Util.getMBeanServer(); + if(server == null) + throw new Exception("No MBeanServer found; JChannelFactory needs to be run with an MBeanServer present, " + + "e.g. inside JBoss or JDK 5, or with ExposeChannel set to false"); + if(domain == null) + domain="jgroups"; + } + } + + public void start() throws Exception { + + } + + public void stop() { + + } + + public void destroy() { + synchronized (channels) { + for(Map.Entry entry: channels.entrySet()){ + Multiplexer m = entry.getValue(); + if(m != null){ + m.closeAll(); + m.close(); + } + } + } + unregister(domain + ":*"); + channels.clear(); + } + + + public String dumpConfiguration() { + return stacks.keySet().toString(); + } + + public String dumpChannels() { + StringBuilder sb = new StringBuilder(); + synchronized (channels) { + for (Map.Entry entry : channels.entrySet()) { + Multiplexer m = entry.getValue(); + sb.append(entry.getKey()).append(": ").append(m.getServiceIds()).append("\n"); + } + } + return sb.toString(); + } + + private void registerChannel(JChannel ch, String stack_name) throws Exception { + if(expose_channels && server != null) + JmxConfigurator.registerChannel(ch, server, domain, stack_name, expose_protocols); + } + + + private void unregister(String name) { + if(expose_channels && server != null){ + try{ + JmxConfigurator.unregister(server, name); + }catch(Exception e){ + log.error("failed unregistering " + name, e); + } + } + } + + + + private void parse(InputStream input, boolean replace) throws Exception { + /** + * CAUTION: crappy code ahead ! I (bela) am not an XML expert, so the code below is pretty amateurish... + * But it seems to work, and it is executed only on startup, so no perf loss on the critical path. + * If somebody wants to improve this, please be my guest. + */ + DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance(); + factory.setValidating(false); //for now + DocumentBuilder builder=factory.newDocumentBuilder(); + Document document=builder.parse(input); + + // The root element of the document should be the "config" element, + // but the parser(Element) method checks this so a check is not + // needed here. + Element configElement = document.getDocumentElement(); + parse(configElement, replace); + } + + private void parse(Element root, boolean replace) throws Exception { + /** + * CAUTION: crappy code ahead ! I (bela) am not an XML expert, so the code below is pretty amateurish... + * But it seems to work, and it is executed only on startup, so no perf loss on the critical path. + * If somebody wants to improve this, please be my guest. + */ + String root_name=root.getNodeName(); + if(!PROTOCOL_STACKS.equals(root_name.trim().toLowerCase())) { + String error="XML protocol stack configuration does not start with a '' element; " + + "maybe the XML configuration needs to be converted to the new format ?\n" + + "use 'java org.jgroups.conf.XmlConfigurator -new_format' to do so"; + throw new IOException("invalid XML configuration: " + error); + } + + NodeList tmp_stacks=root.getChildNodes(); + for(int i=0; i < tmp_stacks.getLength(); i++) { + Node node = tmp_stacks.item(i); + if(node.getNodeType() != Node.ELEMENT_NODE ) + continue; + + Element stack=(Element) node; + String tmp=stack.getNodeName(); + if(!STACK.equals(tmp.trim().toLowerCase())) { + throw new IOException("invalid configuration: didn't find a \"" + STACK + "\" element under \"" + PROTOCOL_STACKS + "\""); + } + + NamedNodeMap attrs = stack.getAttributes(); + Node name=attrs.getNamedItem(NAME); + // Node descr=attrs.getNamedItem(DESCR); + String st_name=name.getNodeValue(); + // String stack_descr=descr.getNodeValue(); + // System.out.print("Parsing \"" + st_name + "\" (" + stack_descr + ")"); + NodeList configs=stack.getChildNodes(); + for(int j=0; j < configs.getLength(); j++) { + Node tmp_config=configs.item(j); + if(tmp_config.getNodeType() != Node.ELEMENT_NODE ) + continue; + Element cfg = (Element) tmp_config; + tmp=cfg.getNodeName(); + if(!CONFIG.equals(tmp)) + throw new IOException("invalid configuration: didn't find a \"" + CONFIG + "\" element under \"" + STACK + "\""); + + XmlConfigurator conf=XmlConfigurator.getInstance(cfg); + // fixes http://jira.jboss.com/jira/browse/JGRP-290 + ConfiguratorFactory.substituteVariables(conf); // replace vars with system props + String val=conf.getProtocolStackString(); + if(replace) { + stacks.put(st_name, val); + if(log.isTraceEnabled()) + log.trace("added config '" + st_name + "'"); + } + else { + if(!stacks.containsKey(st_name)) { + stacks.put(st_name, val); + if(log.isTraceEnabled()) + log.trace("added config '" + st_name + "'"); + } + else { + if(log.isTraceEnabled()) + log.trace("didn't add config '" + st_name + " because one of the same name already existed"); + } + } + } + } + } + + + + private class MuxFactoryChannelListener extends ChannelListenerAdapter{ + + public void channelClosed(Channel channel) { + MuxChannel mch = (MuxChannel)channel; + Multiplexer multiplexer = mch.getMultiplexer(); + boolean all_closed = multiplexer.close(); + if(all_closed) { + channels.remove(mch.getStackName()); + unregister(domain + ":*,cluster=" + mch.getStackName()); + } + } + } +} Index: 3rdParty_sources/jgroups/org/jgroups/Membership.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/Membership.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/Membership.java 17 Aug 2012 14:51:05 -0000 1.1 @@ -0,0 +1,256 @@ +// $Id: Membership.java,v 1.1 2012/08/17 14:51:05 marcin Exp $ + +package org.jgroups; + + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.*; + + +/** + * Class to keep track of Addresses. + * The membership object holds a vector of Address objects that are in the same membership. + * Each unique address can only exist once; i.e., doing Membership.add(existing_address) + * will be ignored. + */ +public class Membership implements Cloneable { + /* private vector to hold all the addresses */ + private final List
members=new LinkedList
(); + protected static final Log log=LogFactory.getLog(Membership.class); + + /** + * Public constructor + * Creates a member ship object with zero members + */ + public Membership() { + } + + + /** + * Creates a member ship object with the initial members. + * The Address references are copied out of the vector, so that the + * vector passed in as parameters is not the same reference as the vector + * that the membership class is using + * + * @param initial_members - a list of members that belong to this membership + */ + public Membership(Collection
initial_members) { + if(initial_members != null) + add(initial_members); + } + + + + /** + * returns a copy (clone) of the members in this membership. + * the vector returned is immutable in reference to this object. + * ie, modifying the vector that is being returned in this method + * will not modify this membership object. + * + * @return a list of members, + */ + public Vector
getMembers() { + /*clone so that this objects members can not be manipulated from the outside*/ + synchronized(members) { + return new Vector
(members); + } + } + + + /** + * Adds a new member to this membership. + * If the member already exist (Address.equals(Object) returns true then the member will + * not be added to the membership + */ + public void add(Address new_member) { + synchronized(members) { + if(new_member != null && !members.contains(new_member)) { + members.add(new_member); + } + } + } + + + /** + * Adds a list of members to this membership + * + * @param v - a vector containing Address objects + * @throws ClassCastException if v contains objects that don't implement the Address interface + * @see #add + */ + public final void add(Collection
v) { + if(v != null) { + for(Iterator
it=v.iterator(); it.hasNext();) { + Address addr=it.next(); + add(addr); + } + } + } + + + /** + * removes an member from the membership. + * If this member doesn't exist, no action will be performed on the existing membership + * + * @param old_member - the member to be removed + */ + public void remove(Address old_member) { + if(old_member != null) { + synchronized(members) { + members.remove(old_member); + } + } + } + + + /** + * removes all the members contained in v from this membership + * + * @param v - a vector containing all the members to be removed + */ + public void remove(Collection
v) { + if(v != null) { + synchronized(members) { + members.removeAll(v); + } + } + } + + + /** + * removes all the members from this membership + */ + public void clear() { + synchronized(members) { + members.clear(); + } + } + + /** + * Clear the membership and adds all members of v + * This method will clear out all the old members of this membership by + * invoking the Clear method. + * Then it will add all the all members provided in the vector v + * + * @param v - a vector containing all the members this membership will contain + */ + public void set(Collection
v) { + clear(); + if(v != null) { + add(v); + } + } + + + /** + * Clear the membership and adds all members of v + * This method will clear out all the old members of this membership by + * invoking the Clear method. + * Then it will add all the all members provided in the vector v + * + * @param m - a membership containing all the members this membership will contain + */ + public void set(Membership m) { + clear(); + if(m != null) { + add(m.getMembers()); + } + } + + + /** + * merges membership with the new members and removes suspects + * The Merge method will remove all the suspects and add in the new members. + * It will do it in the order + * 1. Remove suspects + * 2. Add new members + * the order is very important to notice. + * + * @param new_mems - a vector containing a list of members (Address) to be added to this membership + * @param suspects - a vector containing a list of members (Address) to be removed from this membership + */ + public void merge(Collection
new_mems, Collection
suspects) { + remove(suspects); + add(new_mems); + } + + + /** + * Returns true if the provided member belongs to this membership + * + * @param member + * @return true if the member belongs to this membership + */ + public boolean contains(Address member) { + if(member == null) return false; + synchronized(members) { + return members.contains(member); + } + } + + + /* Simple inefficient bubble sort, but not used very often (only when merging) */ + public void sort() { + synchronized(members) { + Collections.sort(members); + } + } + + + + + /** + * returns a copy of this membership + * + * @return an exact copy of this membership + */ + public Membership copy() { + return ((Membership)clone()); + } + + + /** + * @return a clone of this object. The list of members is copied to a new + * container + */ + public Object clone() { + return new Membership(this.members); + } + + + /** + * Returns the number of addresses in this membership + * + * @return the number of addresses in this membership + */ + public int size() { + synchronized(members) { + return members.size(); + } + } + + /** + * Returns the component at the specified index + * + * @param index - 0..size()-1 + * @throws ArrayIndexOutOfBoundsException - if the index is negative or not less than the current size of this Membership object. + * @see java.util.Vector#elementAt + */ + + public Address elementAt(int index) { + synchronized(members) { + return members.get(index); + } + } + + + public String toString() { + synchronized(members) { + return members.toString(); + } + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/MembershipListener.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/MembershipListener.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/MembershipListener.java 17 Aug 2012 14:51:04 -0000 1.1 @@ -0,0 +1,45 @@ +// $Id: MembershipListener.java,v 1.1 2012/08/17 14:51:04 marcin Exp $ + +package org.jgroups; + + + +/** + * Allows a listener to be notified when group membership changes. + * These callbacks are used in {@link org.jgroups.blocks.PullPushAdapter}. + *

+ * The MembershipListener interface is similar to the {@link MessageListener} + * interface: every time a new view, a suspicion message, or a + * block event is received, the corresponding method of the class implementing + * MembershipListener will be called. + * Oftentimes the only method containing any functionality will be viewAccepted() + * which notifies the receiver that a new member has joined the group or that an + * existing member has left or crashed. + */ +public interface MembershipListener { + + /** + * Called when a change in membership has occurred. + * No long running actions or sending of messages should be done in this callback. + * If some long running action needs to be performed, it should be done in a separate thread.

+ * Note that on reception of the first view (a new member just joined), the channel will not yet be + * in the connected state. This only happens when {@link Channel#connect(String)} returns. + */ + void viewAccepted(View new_view); + + /** + * Called whenever a member is suspected of having crashed, + * but has not yet been excluded. + */ + void suspect(Address suspected_mbr); + + /** + * Called (usually by the FLUSH protocol), as an indication that the member should stop sending messages. + * Any messages sent after returning from this callback might get blocked by the FLUSH protocol. When the FLUSH + * protocol is done, and messages can be sent again, the FLUSH protocol will simply unblock all pending messages. + * If a callback for unblocking is desired, implement {@link org.jgroups.ExtendedMembershipListener#unblock()}. + * Note that block() is the equivalent of reception of a BlockEvent in the pull mode. + */ + void block(); + +} Index: 3rdParty_sources/jgroups/org/jgroups/MergeView.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/MergeView.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/MergeView.java 17 Aug 2012 14:51:04 -0000 1.1 @@ -0,0 +1,142 @@ +// $Id: MergeView.java,v 1.1 2012/08/17 14:51:04 marcin Exp $ + + +package org.jgroups; + +import java.io.*; +import java.util.Vector; + + +/** + * A view that is sent as a result of a merge. + * Whenever a group splits into subgroups, e.g., due to a network partition, + * and later the subgroups merge back together, a MergeView instead of a View + * will be received by the application. The MergeView class is a subclass of + * View and contains as additional instance variable: the list of views that + * were merged. For example, if the group denoted by view V1:(p,q,r,s,t) + * splits into subgroups V2:(p,q,r) and V2:(s,t), the merged view might be + * V3:(p,q,r,s,t). In this case the MergeView would contain a list of 2 views: + * V2:(p,q,r) and V2:(s,t). + */ +public class MergeView extends View { + protected Vector subgroups=null; // subgroups that merged into this single view (a list of Views) + + + /** + * Used by externalization + */ + public MergeView() { + } + + + /** + * Creates a new view + * + * @param vid The view id of this view (can not be null) + * @param members Contains a list of all the members in the view, can be empty but not null. + * @param subgroups A list of Views representing the former subgroups + */ + public MergeView(ViewId vid, Vector

members, Vector subgroups) { + super(vid, members); + this.subgroups=subgroups; + } + + + /** + * Creates a new view + * + * @param creator The creator of this view (can not be null) + * @param id The lamport timestamp of this view + * @param members Contains a list of all the members in the view, can be empty but not null. + * @param subgroups A list of Views representing the former subgroups + */ + public MergeView(Address creator, long id, Vector
members, Vector subgroups) { + super(creator, id, members); + this.subgroups=subgroups; + } + + + public Vector getSubgroups() { + return subgroups; + } + + + /** + * creates a copy of this view + * + * @return a copy of this view + */ + public Object clone() { + ViewId vid2=vid != null ? (ViewId)vid.clone() : null; + Vector
members2=members != null ? (Vector
)members.clone() : null; + Vector subgroups2=subgroups != null ? (Vector)subgroups.clone() : null; + return new MergeView(vid2, members2, subgroups2); + } + + + public String toString() { + StringBuilder sb=new StringBuilder(); + sb.append("MergeView::").append(super.toString()).append(", subgroups=").append(subgroups); + return sb.toString(); + } + + + public void writeExternal(ObjectOutput out) throws IOException { + super.writeExternal(out); + out.writeObject(subgroups); + } + + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + super.readExternal(in); + subgroups=(Vector)in.readObject(); + } + + + public void writeTo(DataOutputStream out) throws IOException { + super.writeTo(out); + + // write subgroups + int len=subgroups != null? subgroups.size() : 0; + out.writeShort(len); + if(len == 0) + return; + for(View v: subgroups) { + if(v instanceof MergeView) + out.writeBoolean(true); + else + out.writeBoolean(false); + v.writeTo(out); + } + } + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + super.readFrom(in); + short len=in.readShort(); + if(len > 0) { + View v; + subgroups=new Vector(); + for(int i=0; i < len; i++) { + boolean is_merge_view=in.readBoolean(); + v=is_merge_view? new MergeView() : new View(); + v.readFrom(in); + subgroups.add(v); + } + } + } + + public int serializedSize() { + int retval=super.serializedSize(); + retval+=Global.SHORT_SIZE; // for size of subgroups vector + + if(subgroups == null) + return retval; + for(View v: subgroups) { + retval+=Global.BYTE_SIZE; // boolean for View or MergeView + retval+=v.serializedSize(); + } + return retval; + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/Message.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/Message.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/Message.java 17 Aug 2012 14:51:03 -0000 1.1 @@ -0,0 +1,829 @@ + +package org.jgroups; + + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.conf.ClassConfigurator; +import org.jgroups.stack.IpAddress; +import org.jgroups.util.*; + +import java.io.*; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + + +/** + * A Message encapsulates data sent to members of a group. It contains among other things the + * address of the sender, the destination address, a payload (byte buffer) and a list of + * headers. Headers are added by protocols on the sender side and removed by protocols + * on the receiver's side. + *

+ * The byte buffer can point to a reference, and we can subset it using index and length. However, + * when the message is serialized, we only write the bytes between index and length. + * @author Bela Ban + * @version $Id: Message.java,v 1.1 2012/08/17 14:51:03 marcin Exp $ + */ +public class Message implements Externalizable, Streamable { + protected Address dest_addr=null; + protected Address src_addr=null; + + /** The payload */ + private byte[] buf=null; + + /** The index into the payload (usually 0) */ + protected transient int offset=0; + + /** The number of bytes in the buffer (usually buf.length is buf not equal to null). */ + protected transient int length=0; + + /** All headers are placed here */ + protected Headers headers; + + protected static final Log log=LogFactory.getLog(Message.class); + + private static final long serialVersionUID=7966206671974139740L; + + static final byte DEST_SET = 1; + static final byte SRC_SET = 2; + static final byte BUF_SET = 4; + // static final byte HDRS_SET=8; // bela July 15 2005: not needed, we always create headers + static final byte IPADDR_DEST =16; + static final byte IPADDR_SRC =32; + static final byte SRC_HOST_NULL =64; + + + // =========================== Flags ============================== + public static final byte OOB = 1; + public static final byte LOW_PRIO = 2; // not yet sure if we want this flag... + public static final byte HIGH_PRIO = 4; // not yet sure if we want this flag... + + private byte flags=0; + + static final Set nonStreamableHeaders=new HashSet(); + + /** Map. Maintains mappings to canonical addresses */ + private static final ConcurrentHashMap canonicalAddresses=new ConcurrentHashMap(); + private static final boolean DISABLE_CANONICALIZATION; + + static { + boolean b; + try { + b=Boolean.getBoolean("disable_canonicalization"); + } + catch (java.security.AccessControlException e) { + // this will happen in an applet context + b=true; + } + DISABLE_CANONICALIZATION=b; + } + + + /** Public constructor + * @param dest Address of receiver. If it is null then the message sent to the group. + * Otherwise, it contains a single destination and is sent to that member.

+ */ + public Message(Address dest) { + setDest(dest); + headers=createHeaders(3); + } + + /** Public constructor + * @param dest Address of receiver. If it is null then the message sent to the group. + * Otherwise, it contains a single destination and is sent to that member.

+ * @param src Address of sender + * @param buf Message to be sent. Note that this buffer must not be modified (e.g. buf[0]=0 is + * not allowed), since we don't copy the contents on clopy() or clone(). + */ + public Message(Address dest, Address src, byte[] buf) { + this(dest); + setSrc(src); + setBuffer(buf); + } + + + /** + * Constructs a message. The index and length parameters allow to provide a reference to + * a byte buffer, rather than a copy, and refer to a subset of the buffer. This is important when + * we want to avoid copying. When the message is serialized, only the subset is serialized.
+ * + * Note that the byte[] buffer passed as argument must not be modified. Reason: if we retransmit the + * message, it would still have a ref to the original byte[] buffer passed in as argument, and so we would + * retransmit a changed byte[] buffer ! + * + * @param dest Address of receiver. If it is null then the message sent to the group. + * Otherwise, it contains a single destination and is sent to that member.

+ * @param src Address of sender + * @param buf A reference to a byte buffer + * @param offset The index into the byte buffer + * @param length The number of bytes to be used from buf. Both index and length are checked for + * array index violations and an ArrayIndexOutOfBoundsException will be thrown if invalid + */ + public Message(Address dest, Address src, byte[] buf, int offset, int length) { + this(dest); + setSrc(src); + setBuffer(buf, offset, length); + } + + + /** Public constructor + * @param dest Address of receiver. If it is null then the message sent to the group. + * Otherwise, it contains a single destination and is sent to that member.

+ * @param src Address of sender + * @param obj The object will be serialized into the byte buffer. Object + * has to be serializable ! The resulting buffer must not be modified + * (e.g. buf[0]=0 is not allowed), since we don't copy the contents on clopy() or clone().

+ * Note that this is a convenience method and JGroups will use default Java serialization to + * serialize obj into a byte buffer. + */ + public Message(Address dest, Address src, Serializable obj) { + this(dest); + setSrc(src); + setObject(obj); + } + + + public Message() { + headers=createHeaders(3); + } + + + public Message(boolean create_headers) { + if(create_headers) + headers=createHeaders(3); + } + + public Address getDest() { + return dest_addr; + } + + public void setDest(Address new_dest) { + if(DISABLE_CANONICALIZATION) + dest_addr=new_dest; + else + dest_addr=canonicalAddress(new_dest); + } + + public Address getSrc() { + return src_addr; + } + + public void setSrc(Address new_src) { + if(DISABLE_CANONICALIZATION) + src_addr=new_src; + else + src_addr=canonicalAddress(new_src); + } + + /** + * Returns a reference to the payload (byte buffer). Note that this buffer should not be modified as + * we do not copy the buffer on copy() or clone(): the buffer of the copied message is simply a reference to + * the old buffer.
+ * Even if offset and length are used: we return the entire buffer, not a subset. + */ + public byte[] getRawBuffer() { + return buf; + } + + /** + * Returns a copy of the buffer if offset and length are used, otherwise a reference. + * @return byte array with a copy of the buffer. + */ + final public byte[] getBuffer() { + if(buf == null) + return null; + if(offset == 0 && length == buf.length) + return buf; + else { + byte[] retval=new byte[length]; + System.arraycopy(buf, offset, retval, 0, length); + return retval; + } + } + + final public void setBuffer(byte[] b) { + buf=b; + if(buf != null) { + offset=0; + length=buf.length; + } + else { + offset=length=0; + } + } + + /** + * Set the internal buffer to point to a subset of a given buffer + * @param b The reference to a given buffer. If null, we'll reset the buffer to null + * @param offset The initial position + * @param length The number of bytes + */ + final public void setBuffer(byte[] b, int offset, int length) { + buf=b; + if(buf != null) { + if(offset < 0 || offset > buf.length) + throw new ArrayIndexOutOfBoundsException(offset); + if((offset + length) > buf.length) + throw new ArrayIndexOutOfBoundsException((offset+length)); + this.offset=offset; + this.length=length; + } + else { + this.offset=this.length=0; + } + } + + /** + + * Note that the byte[] buffer passed as argument must not be modified. Reason: if we retransmit the + * message, it would still have a ref to the original byte[] buffer passed in as argument, and so we would + * retransmit a changed byte[] buffer ! + * + */ + public final void setBuffer(Buffer buf) { + if(buf != null) { + this.buf=buf.getBuf(); + this.offset=buf.getOffset(); + this.length=buf.getLength(); + } + } + + + /** Returns the offset into the buffer at which the data starts */ + public int getOffset() { + return offset; + } + + /** Returns the number of bytes in the buffer */ + public int getLength() { + return length; + } + + /** Returns a reference to the headers hashmap, which is immutable. Any attempt to + * modify the returned map will cause a runtime exception */ + public Map getHeaders() { + return headers.getHeaders(); + } + + public String printHeaders() { + return headers.printHeaders(); + } + + public int getNumHeaders() { + return headers.size(); + } + + /** + * Takes an object and uses Java serialization to generate the byte[] buffer which is set in the message. + */ + final public void setObject(Serializable obj) { + if(obj == null) return; + try { + byte[] tmp=Util.objectToByteBuffer(obj); + setBuffer(tmp); + } + catch(Exception ex) { + throw new IllegalArgumentException(ex); + } + } + + /** + * Uses Java serialization to create an object from the buffer of the message. Note that this is dangerous when + * using your own classloader, e.g. inside of an application server ! Most likely, JGroups will use the system + * classloader to deserialize the buffer into an object, whereas (for example) a web application will want to + * use the webapp's classloader, resulting in a ClassCastException. The recommended way is for the application to + * use their own serialization and only pass byte[] buffer to JGroups. + * @return + */ + final public Object getObject() { + try { + return Util.objectFromByteBuffer(buf, offset, length); + } + catch(Exception ex) { + throw new IllegalArgumentException(ex); + } + } + + + public void setFlag(byte flag) { + if(flag > Byte.MAX_VALUE || flag < 0) + throw new IllegalArgumentException("flag has to be >= 0 and <= " + Byte.MAX_VALUE); + flags |= flag; + } + + public void clearFlag(byte flag) { + if(flag > Byte.MAX_VALUE || flag < 0) + throw new IllegalArgumentException("flag has to be >= 0 and <= " + Byte.MAX_VALUE); +// if(isFlagSet(flag)) { +// flags ^= flag; +// } + flags &= ~flag; + } + + public boolean isFlagSet(byte flag) { + return (flags & flag) == flag; + } + + public byte getFlags() { + return flags; + } + + + /*---------------------- Used by protocol layers ----------------------*/ + + /** Puts a header given a key into the hashmap. Overwrites potential existing entry. */ + public void putHeader(String key, Header hdr) { + headers.putHeader(key, hdr); + } + + /** + * Puts a header given a key into the map, only if the key doesn't exist yet + * @param key + * @param hdr + * @return the previous value associated with the specified key, or + * null if there was no mapping for the key. + * (A null return can also indicate that the map + * previously associated null with the key, + * if the implementation supports null values.) + */ + public Header putHeaderIfAbsent(String key, Header hdr) { + return headers.putHeaderIfAbsent(key, hdr); + } + + /** + * + * @param key + * @return the header assoaicted with key + * @deprecated Use getHeader() instead. The issue with removing a header is described in + * http://jira.jboss.com/jira/browse/JGRP-393 + */ + public Header removeHeader(String key) { + return getHeader(key); + } + + public Header getHeader(String key) { + return headers.getHeader(key); + } + /*---------------------------------------------------------------------*/ + + + public Message copy() { + return copy(true); + } + + /** + * Create a copy of the message. If offset and length are used (to refer to another buffer), the copy will + * contain only the subset offset and length point to, copying the subset into the new copy. + * @param copy_buffer + * @return Message with specified data + */ + public Message copy(boolean copy_buffer) { + Message retval=new Message(false); + retval.dest_addr=dest_addr; + retval.src_addr=src_addr; + retval.flags=flags; + + if(copy_buffer && buf != null) { + + // change bela Feb 26 2004: we don't resolve the reference + retval.setBuffer(buf, offset, length); + } + + retval.headers=createHeaders(headers); + return retval; + } + + + protected Object clone() throws CloneNotSupportedException { + return copy(); + } + + public Message makeReply() { + return new Message(src_addr); + } + + + public String toString() { + StringBuilder ret=new StringBuilder(64); + ret.append("[dst: "); + if(dest_addr == null) + ret.append(""); + else + ret.append(dest_addr); + ret.append(", src: "); + if(src_addr == null) + ret.append(""); + else + ret.append(src_addr); + + int size; + if((size=getNumHeaders()) > 0) + ret.append(" (").append(size).append(" headers)"); + + ret.append(", size="); + if(buf != null && length > 0) + ret.append(length); + else + ret.append('0'); + ret.append(" bytes"); + if(flags > 0) + ret.append(", flags=").append(flagsToString()); + ret.append(']'); + return ret.toString(); + } + + + + + /** Tries to read an object from the message's buffer and prints it */ + public String toStringAsObject() { + + if(buf == null) return null; + try { + Object obj=getObject(); + return obj != null ? obj.toString() : ""; + } + catch(Exception e) { // it is not an object + return ""; + } + } + + + /** + * Returns size of buffer, plus some constant overhead for src and dest, plus number of headers time + * some estimated size/header. The latter is needed because we don't want to marshal all headers just + * to find out their size requirements. If a header implements Sizeable, the we can get the correct + * size.

Size estimations don't have to be very accurate since this is mainly used by FRAG to + * determine whether to fragment a message or not. Fragmentation will then serialize the message, + * therefore getting the correct value. + */ + + + /** + * Returns the exact size of the marshalled message. Uses method size() of each header to compute the size, so if + * a Header subclass doesn't implement size() we will use an approximation. However, most relevant header subclasses + * have size() implemented correctly. (See org.jgroups.tests.SizeTest). + * @return The number of bytes for the marshalled message + */ + public long size() { + long retval=Global.BYTE_SIZE // leading byte + + Global.BYTE_SIZE // flags + + length // buffer + + (buf != null? Global.INT_SIZE : 0); // if buf != null 4 bytes for length + + // if(dest_addr != null) + // retval+=dest_addr.size(); + if(src_addr != null) + retval+=(src_addr).size(); + + retval+=Global.SHORT_SIZE; // size (short) + retval+=headers.marshalledSize(); + return retval; + } + + + public String printObjectHeaders() { + return headers.printObjectHeaders(); + } + + + + /* ----------------------------------- Interface Externalizable ------------------------------- */ + + public void writeExternal(ObjectOutput out) throws IOException { + int len; + Externalizable hdr; + Map.Entry entry; + + if(dest_addr != null) { + out.writeBoolean(true); + Marshaller.write(dest_addr, out); + } + else { + out.writeBoolean(false); + } + + if(src_addr != null) { + out.writeBoolean(true); + Marshaller.write(src_addr, out); + } + else { + out.writeBoolean(false); + } + + out.write(flags); + + if(buf == null) + out.writeInt(0); + else { + out.writeInt(length); + out.write(buf, offset, length); + } + + len=headers.size(); + out.writeInt(len); + final Object[] data=headers.getRawData(); + + for(int i=0; i < data.length; i+=2) { + if(data[i] != null) { + out.writeUTF((String)data[i]); + hdr=(Externalizable)data[i+1]; + Marshaller.write(hdr, out); + } + } + } + + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + boolean destAddressExist=in.readBoolean(); + + if(destAddressExist) { + dest_addr=(Address)Marshaller.read(in); + if(!DISABLE_CANONICALIZATION) + dest_addr=canonicalAddress(dest_addr); + } + + boolean srcAddressExist=in.readBoolean(); + if(srcAddressExist) { + src_addr=(Address)Marshaller.read(in); + if(!DISABLE_CANONICALIZATION) + src_addr=canonicalAddress(src_addr); + } + + flags=in.readByte(); + + int i=in.readInt(); + if(i != 0) { + buf=new byte[i]; + in.readFully(buf); + offset=0; + length=buf.length; + } + + int len=in.readInt(); + while(len-- > 0) { + String key=in.readUTF(); + Header value=(Header)Marshaller.read(in); + headers.putHeader(key, value); + } + } + + /* --------------------------------- End of Interface Externalizable ----------------------------- */ + + + /* ----------------------------------- Interface Streamable ------------------------------- */ + + /** + * Streams all members (dest and src addresses, buffer and headers) to the output stream. + * @param out + * @throws IOException + */ + public void writeTo(DataOutputStream out) throws IOException { + byte leading=0; + + if(src_addr != null) { + leading+=SRC_SET; + if(src_addr instanceof IpAddress) { + leading+=IPADDR_SRC; + if(((IpAddress)src_addr).getIpAddress() == null) { + leading+=SRC_HOST_NULL; + } + } + } + if(buf != null) + leading+=BUF_SET; + + // 1. write the leading byte first + out.write(leading); + + // the flags (e.g. OOB, LOW_PRIO) + out.write(flags); + + // 3. src_addr + if(src_addr != null) { + if(src_addr instanceof IpAddress) { + src_addr.writeTo(out); + } + else { + Util.writeAddress(src_addr, out); + } + } + + // 4. buf + if(buf != null) { + out.writeInt(length); + out.write(buf, offset, length); + } + + // 5. headers + int size=headers.size(); + out.writeShort(size); + final Object[] data=headers.getRawData(); + for(int i=0; i < data.length; i+=2) { + if(data[i] != null) { + out.writeUTF((String)data[i]); + writeHeader((Header)data[i+1], out); + } + } + } + + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + int len, leading; + String hdr_name; + Header hdr; + + + // 1. read the leading byte first + leading=in.readByte(); + + flags=in.readByte(); + + // 2. src_addr + if((leading & SRC_SET) == SRC_SET) { + if((leading & IPADDR_SRC) == IPADDR_SRC) { + src_addr=new IpAddress(); + src_addr.readFrom(in); + } + else { + src_addr=Util.readAddress(in); + } + if(!DISABLE_CANONICALIZATION) + src_addr=canonicalAddress(src_addr); + } + + // 3. buf + if((leading & BUF_SET) == BUF_SET) { + len=in.readInt(); + buf=new byte[len]; + in.read(buf, 0, len); + length=len; + } + + // 4. headers + len=in.readShort(); + headers=createHeaders(len); + Object[] data=headers.getRawData(); + int index=0; + for(int i=0; i < len; i++) { + hdr_name=in.readUTF(); + data[index++]=hdr_name; + hdr=readHeader(in); + data[index++]=hdr; + // headers.putHeader(hdr_name, hdr); + } + } + + + + /* --------------------------------- End of Interface Streamable ----------------------------- */ + + + + /* ----------------------------------- Private methods ------------------------------- */ + + private String flagsToString() { + StringBuilder sb=new StringBuilder(); + boolean first=true; + if(isFlagSet(OOB)) { + first=false; + sb.append("OOB"); + } + if(isFlagSet(LOW_PRIO)) { + if(!first) + sb.append("|"); + else + first=false; + sb.append("LOW_PRIO"); + } + if(isFlagSet(HIGH_PRIO)) { + if(!first) + sb.append("|"); + else + first=false; + sb.append("HIGH_PRIO"); + } + return sb.toString(); + } + + private static void writeHeader(Header value, DataOutputStream out) throws IOException { + short magic_number; + String classname; + ObjectOutputStream oos=null; + int size=value.size(); + try { + magic_number=ClassConfigurator.getInstance(false).getMagicNumber(value.getClass()); + // write the magic number or the class name + out.writeShort(magic_number); + if(magic_number == -1) { + classname=value.getClass().getName(); + out.writeUTF(classname); + if(log.isWarnEnabled()) + log.warn("magic number for " + classname + " not found, make sure you add your header to " + + "jg-magic-map.xml, or register it programmatically with the ClassConfigurator"); + } + + out.writeShort(size); + + // write the contents + if(value instanceof Streamable) { + ((Streamable)value).writeTo(out); + } + else { + oos=new ObjectOutputStream(out); + value.writeExternal(oos); + if(!nonStreamableHeaders.contains(value.getClass())) { + nonStreamableHeaders.add(value.getClass()); + if(log.isTraceEnabled()) + log.trace("encountered non-Streamable header: " + value.getClass()); + } + } + } + catch(ChannelException e) { + IOException io_ex=new IOException("failed writing header"); + io_ex.initCause(e); + throw io_ex; + } + finally { + if(oos != null) + oos.close(); // this is a no-op on ByteArrayOutputStream + } + } + + + private static Header readHeader(DataInputStream in) throws IOException { + Header hdr; + short magic_number; + String classname; + Class clazz; + ObjectInputStream ois=null; + + try { + magic_number=in.readShort(); + if(magic_number != -1) { + clazz=ClassConfigurator.getInstance(false).get(magic_number); + if(clazz == null) + throw new IllegalArgumentException("magic number " + magic_number + " is not available in magic map"); + } + else { + classname=in.readUTF(); + clazz=ClassConfigurator.getInstance(false).get(classname); + } + + in.readShort(); // we discard the size since we don't use it + + hdr=(Header)clazz.newInstance(); + if(hdr instanceof Streamable) { + ((Streamable)hdr).readFrom(in); + } + else { + ois=new ObjectInputStream(in); + hdr.readExternal(ois); + } + } + catch(Exception ex) { + IOException io_ex=new IOException("failed reading header"); + io_ex.initCause(ex); + throw io_ex; + } + return hdr; + } + + private static Headers createHeaders(int size) { + return size > 0? new Headers(size) : new Headers(3); + } + + + private static Headers createHeaders(Headers m) { + return new Headers(m); + } + + /** canonicalize addresses to some extent. There are race conditions + * allowed in this method, so it may not fully canonicalize an address + * @param nonCanonicalAddress + * @return canonical representation of the address + */ + private static Address canonicalAddress(Address nonCanonicalAddress) { + Address result=null; + if(nonCanonicalAddress == null) { + return null; + } + // do not synchronize between get/put on the canonical map to avoid cost of contention + // this can allow multiple equivalent addresses to leak out, but it's worth the cost savings + try { + result=canonicalAddresses.putIfAbsent(nonCanonicalAddress, nonCanonicalAddress); + return result != null? result : nonCanonicalAddress; + } + catch(NullPointerException npe) { + // no action needed + } + return result; + } + + /* ------------------------------- End of Private methods ---------------------------- */ + + + +} Index: 3rdParty_sources/jgroups/org/jgroups/MessageListener.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/MessageListener.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/MessageListener.java 17 Aug 2012 14:51:05 -0000 1.1 @@ -0,0 +1,30 @@ +// $Id: MessageListener.java,v 1.1 2012/08/17 14:51:05 marcin Exp $ + +package org.jgroups; + +/** + * Allows a listener to be notified when a message arrives. + * Contrary to the pull-style of channels, some building blocks + * (e.g., {@link org.jgroups.blocks.PullPushAdapter}) provide an + * event-like, push-style message delivery model. + * In this case, the entity to be notified of message reception needs to + * provide a callback to be invoked whenever a message has been received. + * The MessageListener interface provides a method to do so. + */ +public interface MessageListener { + /** + * Called when a message is received. + * @param msg + */ + void receive(Message msg); + /** + * Answers the group state; e.g., when joining. + * @return byte[] + */ + byte[] getState(); + /** + * Sets the group state; e.g., when joining. + * @param state + */ + void setState(byte[] state); +} Index: 3rdParty_sources/jgroups/org/jgroups/Receiver.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/Receiver.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/Receiver.java 17 Aug 2012 14:51:04 -0000 1.1 @@ -0,0 +1,9 @@ +package org.jgroups; + +/** + * Defines the callbacks that are invoked when messages, views etc are received on a channel + * @author Bela Ban + * @version $Id: Receiver.java,v 1.1 2012/08/17 14:51:04 marcin Exp $ + */ +public interface Receiver extends MessageListener, MembershipListener { +} Index: 3rdParty_sources/jgroups/org/jgroups/ReceiverAdapter.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/ReceiverAdapter.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/ReceiverAdapter.java 17 Aug 2012 14:51:04 -0000 1.1 @@ -0,0 +1,27 @@ +package org.jgroups; + +/** + * @author Bela Ban + * @version $Id: ReceiverAdapter.java,v 1.1 2012/08/17 14:51:04 marcin Exp $ + */ +public class ReceiverAdapter implements Receiver { + + public void receive(Message msg) { + } + + public byte[] getState() { + return null; + } + + public void setState(byte[] state) { + } + + public void viewAccepted(View new_view) { + } + + public void suspect(Address suspected_mbr) { + } + + public void block() { + } +} Index: 3rdParty_sources/jgroups/org/jgroups/SetStateEvent.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/SetStateEvent.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/SetStateEvent.java 17 Aug 2012 14:51:04 -0000 1.1 @@ -0,0 +1,31 @@ +// $Id: SetStateEvent.java,v 1.1 2012/08/17 14:51:04 marcin Exp $ + +package org.jgroups; + + + + + + +/** + * Encapsulates a state returned by Channel.receive(), as requested by + * Channel.getState(s) previously. + * @author Bela Ban + * @version $Id: SetStateEvent.java,v 1.1 2012/08/17 14:51:04 marcin Exp $ + */ +public class SetStateEvent { + byte[] state=null; + String state_id=null; + + public SetStateEvent(byte[] state, String state_id) { + this.state=state; + this.state_id=state_id; + } + + + public byte[] getArg() {return state;} + public String getStateId() {return state_id;} + + public String toString() {return "SetStateEvent[state=" + state + ", state_id=" + state_id + ']';} + +} Index: 3rdParty_sources/jgroups/org/jgroups/StateTransferException.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/StateTransferException.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/StateTransferException.java 17 Aug 2012 14:51:05 -0000 1.1 @@ -0,0 +1,28 @@ +package org.jgroups; + + +/** + * StateTransferException is thrown to indicate failure of + * state transfer between cluster members. + *

+ * + * @author Vladimir Blagojevic + * @since 2.6 + * + */ +public class StateTransferException extends ChannelException { + + private static final long serialVersionUID = -4070956583392020498L; + + public StateTransferException(){ + } + + public StateTransferException(String reason){ + super(reason); + } + + public StateTransferException(String reason,Throwable cause){ + super(reason, cause); + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/StreamingGetStateEvent.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/StreamingGetStateEvent.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/StreamingGetStateEvent.java 17 Aug 2012 14:51:04 -0000 1.1 @@ -0,0 +1,88 @@ +package org.jgroups; + +import java.io.OutputStream; + +/** + * + * Represents an event returned by channel.receive(), as a result + * of another channel instance requesting a state from this channel. Other channel + * has to invoke channel.getState() indicating intent of state + * retrieval. + * + *

+ * + * Allows applications using a channel in a pull mode to receive + * StreamingGetStateEvent event and thus provide state to requsting + * channel instance. Channels have to be configured with + * STREAMING_STATE_TRANSFER protocol rather than the default + * STATE_TRANSFER protocol in order to receive this event + * + *

+ * + * The following code demonstrates how to pull events from a channel, processing + * StreamingGetStateEvent and sending hypothetical state through + * OutputStream reference. + * + *

+ *  Object obj=channel.receive(0);
+ *  if(obj instanceof StreamingGetStateEvent) {
+ *   	StreamingGetStateEvent evt=(StreamingGetStateEvent)obj;
+ *    	OutputStream oos = null;
+ *		try {			
+ *			oos = new ObjectOutputStream(evt.getArg());			
+ *			oos.writeObject(state);   
+ *	    	oos.flush();
+ *		} catch (Exception e) {} 
+ *		finally
+ *		{
+ *			try {				
+ *				oos.close();
+ *			} catch (IOException e) {
+ *				System.err.println(e);
+ *			}
+ *		}                
+ *   }
+ * 
+ * + * + * @author Vladimir Blagojevic + * @see org.jgroups.JChannel#getState(Address, long) + * @see org.jgroups.StreamingMessageListener#getState(OutputStream) + * @since 2.4 + * + */ + +public class StreamingGetStateEvent { + + OutputStream os; + String state_id; + + public StreamingGetStateEvent(OutputStream os,String state_id) { + super(); + this.os=os; + this.state_id=state_id; + } + /** + * Returns OutputStream used for writing of a state. + * + * @return the OutputStream + */ + public OutputStream getArg() + { + return os; + } + + /** + * Returns id of the partial state if partial state was requested. + * If full state transfer was requested this method will return null. + * + * @see JChannel#getState(Address, long) + * @see JChannel#getState(Address, String, long) + * @return partial state id + */ + public String getStateId() + { + return state_id; + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/StreamingSetStateEvent.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/StreamingSetStateEvent.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/StreamingSetStateEvent.java 17 Aug 2012 14:51:04 -0000 1.1 @@ -0,0 +1,83 @@ +package org.jgroups; +import java.io.InputStream; +/** + * + * Represents an event returned by channel.receive(), as requested by + * channel.getState() previously. + * + *

+ * + * Allows applications using a channel in a pull mode to receive a state from + * another channel instance providing state. Channels have to be configured with + * STREAMING_STATE_TRANSFER protocol rather than the default + * STATE_TRANSFER protocol in order to receive this event. + * + *

+ * + * The following code demonstrate how to pull events from a channel, processing + * StreamingSetStateEvent and retrieving hypothetical state in the + * form of LinkedList from event's InputStream reference. + * + *

+ *  Object obj=channel.receive(0);
+ *  if(obj instanceof StreamingSetStateEvent) {
+ *   	StreamingSetStateEvent evt=(StreamingSetStateEvent)obj;
+ *    	ObjectInputStream ois = null;   	
+ *		try {			
+ *			ois = new ObjectInputStream(evt.getArg());
+ *			state = (LinkedList)ois.readObject();  
+ *		} catch (Exception e) {} 
+ *		finally
+ *		{
+ *			try {				
+ *				ois.close();
+ *			} catch (IOException e) {
+ *				System.err.println(e);
+ *			}
+ *		}                
+ *   }
+ * 
+ * + * + * @author Vladimir Blagojevic + * @see org.jgroups.JChannel#getState(Address, long) + * @see org.jgroups.StreamingMessageListener#setState(InputStream) + * @since 2.4 + * + */ +public class StreamingSetStateEvent { + + InputStream is; + String state_id; + + public StreamingSetStateEvent(InputStream is,String state_id) { + super(); + this.is=is; + this.state_id=state_id; + } + + /** + * Returns InputStream used for reading of a state. + * + * @return the InputStream + */ + public InputStream getArg() + { + return is; + } + + + /** + * Returns id of the partial state if partial state was requested. + * If full state transfer was requested this method will return null. + * + * @see JChannel#getState(Address, long) + * @see JChannel#getState(Address, String, long) + * @return partial state id + */ + public String getStateId() + { + return state_id; + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/SuspectEvent.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/SuspectEvent.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/SuspectEvent.java 17 Aug 2012 14:51:04 -0000 1.1 @@ -0,0 +1,16 @@ +// $Id: SuspectEvent.java,v 1.1 2012/08/17 14:51:04 marcin Exp $ + +package org.jgroups; + +/** + * Represents a suspect event. + * Gives access to the suspected member. + */ +public class SuspectEvent { + final Object suspected_mbr; + + public SuspectEvent(Object suspected_mbr) {this.suspected_mbr=suspected_mbr;} + + public Object getMember() {return suspected_mbr;} + public String toString() {return "SuspectEvent";} +} Index: 3rdParty_sources/jgroups/org/jgroups/SuspectedException.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/SuspectedException.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/SuspectedException.java 17 Aug 2012 14:51:03 -0000 1.1 @@ -0,0 +1,17 @@ +// $Id: SuspectedException.java,v 1.1 2012/08/17 14:51:03 marcin Exp $ + +package org.jgroups; + +/** + * Thrown if a message is sent to a suspected member. + */ +public class SuspectedException extends Exception { + final Object suspect; + + private static final long serialVersionUID=-6663279911010545655L; + + public SuspectedException() {this.suspect=null;} + public SuspectedException(Object suspect) {this.suspect=suspect;} + + public String toString() {return "SuspectedException";} +} Index: 3rdParty_sources/jgroups/org/jgroups/TimeoutException.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/TimeoutException.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/TimeoutException.java 17 Aug 2012 14:51:04 -0000 1.1 @@ -0,0 +1,23 @@ +// $Id: TimeoutException.java,v 1.1 2012/08/17 14:51:04 marcin Exp $ + +package org.jgroups; + +/** + * Thrown if members fail to respond in time. + */ +public class TimeoutException extends Exception { + private static final long serialVersionUID = -3555655828017487825L; + + public TimeoutException() { + super("TimeoutException"); + } + + public TimeoutException(String msg) { + super(msg); + } + + + public String toString() { + return super.toString(); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/Transport.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/Transport.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/Transport.java 17 Aug 2012 14:51:03 -0000 1.1 @@ -0,0 +1,18 @@ +// $Id: Transport.java,v 1.1 2012/08/17 14:51:03 marcin Exp $ + +package org.jgroups; + +/** + * Defines a very small subset of the functionality of a channel, + * essentially only the methods for sending and receiving messages. + * Many building blocks require nothing else than a + * bare-bones facility to send and receive messages; therefore the Transport + * interface was created. It increases the genericness and portability of + * building blocks: being so simple, the Transport interface can easily be + * ported to a different toolkit, without requiring any modifications to + * building blocks. + */ +public interface Transport { + void send(Message msg) throws Exception; + Object receive(long timeout) throws Exception; +} Index: 3rdParty_sources/jgroups/org/jgroups/UnblockEvent.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/UnblockEvent.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/UnblockEvent.java 17 Aug 2012 14:51:04 -0000 1.1 @@ -0,0 +1,10 @@ +package org.jgroups; + +/** + * Trivial object that represents a block event. + * @author Bela Ban + * @version $Id: UnblockEvent.java,v 1.1 2012/08/17 14:51:04 marcin Exp $ + */ +public class UnblockEvent { + public String toString() {return "UnblockEvent";} +} Index: 3rdParty_sources/jgroups/org/jgroups/UpHandler.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/UpHandler.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/UpHandler.java 17 Aug 2012 14:51:03 -0000 1.1 @@ -0,0 +1,14 @@ +// $Id: UpHandler.java,v 1.1 2012/08/17 14:51:03 marcin Exp $ + +package org.jgroups; + +/** + * Provides a way of taking over a channel's tasks. + */ +public interface UpHandler { + /** + * Invoked for all channel events except connection management and state transfer. + * @param evt + */ + Object up(Event evt); +} Index: 3rdParty_sources/jgroups/org/jgroups/Version.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/Version.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/Version.java 17 Aug 2012 14:51:04 -0000 1.1 @@ -0,0 +1,122 @@ + + + +package org.jgroups; + +import org.jgroups.annotations.Immutable; + +/** + * We're using the scheme described at http://www.jboss.com/index.html?module=bb&op=viewtopic&t=77231 + * for major, minor and micro version numbers. We have 5 bits for major and minor version numbers each and + * 6 bits for the micro version. + * This gives: + * X = 0-31 for major versions + * Y = 0-31 for minor versions + * Z = 0-63 for micro versions + * + * @author Bela Ban + * @version $Id: Version.java,v 1.1 2012/08/17 14:51:04 marcin Exp $ + * Holds version information for JGroups. + */ +@Immutable +public class Version { + public static final short major = 2; + public static final short minor = 6; + public static final short micro = 10; + public static final String description="2.6.10.GA"; + + public static final short version=encode(major, minor, micro); + public static final String string_version=print(version); + public static final String cvs="$Id: Version.java,v 1.1 2012/08/17 14:51:04 marcin Exp $"; + + private static final int MAJOR_SHIFT = 11; + private static final int MINOR_SHIFT = 6; + private static final int MAJOR_MASK = 0x00f800; // 1111100000000000 bit mask + private static final int MINOR_MASK = 0x0007c0; // 11111000000 bit mask + private static final int MICRO_MASK = 0x00003f; // 111111 bit mask + + + + /** + * Prints the value of the description and cvs fields to System.out. + * @param args + */ + public static void main(String[] args) { + System.out.println("\nVersion: " + description); + System.out.println("CVS: " + cvs + "\n"); + } + + + /** + * Returns the catenation of the description and cvs fields. + * @return String with description + */ + public static String printDescription() { + return "JGroups " + description + " [" + cvs + "]"; + } + + /** + * Returns the version field as a String. + * @return String with version + */ + public static String printVersion() { + return string_version; + } + + + /** + * Compares the specified version number against the current version number. + * @param v short + * @return Result of == operator. + */ + public static boolean isSame(short v) { + return version == v; + } + + /** Method copied from http://www.jboss.com/index.html?module=bb&op=viewtopic&t=77231 */ + public static short encode(int major, int minor, int micro) { + return (short)((major << MAJOR_SHIFT) + (minor << MINOR_SHIFT) + micro); + } + + /** Method copied from http://www.jboss.com/index.html?module=bb&op=viewtopic&t=77231 */ + public static String print(short version) { + int major=(version & MAJOR_MASK) >> MAJOR_SHIFT; + int minor=(version & MINOR_MASK) >> MINOR_SHIFT; + int micro=(version & MICRO_MASK); + return major + "." + minor + "." + micro; + } + + + public static short[] decode(short version) { + short major=(short)((version & MAJOR_MASK) >> MAJOR_SHIFT); + short minor=(short)((version & MINOR_MASK) >> MINOR_SHIFT); + short micro=(short)(version & MICRO_MASK); + return new short[]{major, minor, micro}; + } + + /** + * Checks whether ver is binary compatible with the current version. The rule for binary compatibility is that + * the major and minor versions have to match, whereas micro versions can differ. + * @param ver + * @return + */ + public static boolean isBinaryCompatible(short ver) { + if(ver == version) + return true; + short[] tmp=decode(ver); + short tmp_major=tmp[0], tmp_minor=tmp[1]; + return major == tmp_major && minor == tmp_minor; + } + + + public static boolean isBinaryCompatible(short ver1, short ver2) { + if(ver1 == ver2) + return true; + short[] tmp=decode(ver1); + short tmp_major=tmp[0], tmp_minor=tmp[1]; + tmp=decode(ver2); + short tmp_major2=tmp[0], tmp_minor2=tmp[1]; + return tmp_major == tmp_major2 && tmp_minor == tmp_minor2; + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/View.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/View.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/View.java 17 Aug 2012 14:51:04 -0000 1.1 @@ -0,0 +1,296 @@ + +package org.jgroups; + + +import org.jgroups.util.Streamable; +import org.jgroups.util.Util; + +import java.io.*; +import java.util.HashMap; +import java.util.Map; +import java.util.Vector; + + +/** + * A view is a local representation of the current membership of a group. + * Only one view is installed in a channel at a time. + * Views contain the address of its creator, an ID and a list of member addresses. + * These adresses are ordered, and the first address is always the coordinator of the view. + * This way, each member of the group knows who the new coordinator will be if the current one + * crashes or leaves the group. + * The views are sent between members using the VIEW_CHANGE event + * @author Bela Ban + * @version $Id: View.java,v 1.1 2012/08/17 14:51:04 marcin Exp $ + */ +public class View implements Externalizable, Cloneable, Streamable { + /* A view is uniquely identified by its ViewID + * The view id contains the creator address and a Lamport time. + * The Lamport time is the highest timestamp seen or sent from a view. + * if a view change comes in with a lower Lamport time, the event is discarded. + */ + protected ViewId vid=null; + + /** + * A list containing all the members of the view + * This list is always ordered, with the coordinator being the first member. + * the second member will be the new coordinator if the current one disappears + * or leaves the group. + */ + protected Vector
members=null; + + protected Map payload=null; + private static final long serialVersionUID=7027860705519930293L; + + + /** + * creates an empty view, should not be used + */ + public View() { + } + + + /** + * Creates a new view + * + * @param vid The view id of this view (can not be null) + * @param members Contains a list of all the members in the view, can be empty but not null. + */ + public View(ViewId vid, Vector
members) { + this.vid=vid; + this.members=members; + } + + + /** + * Creates a new view + * + * @param creator The creator of this view (can not be null) + * @param id The lamport timestamp of this view + * @param members Contains a list of all the members in the view, can be empty but not null. + */ + public View(Address creator, long id, Vector
members) { + this(new ViewId(creator, id), members); + } + + + /** + * returns the view ID of this view + * if this view was created with the empty constructur, null will be returned + * + * @return the view ID of this view + */ + public ViewId getVid() { + return vid; + } + + /** + * returns the creator of this view + * if this view was created with the empty constructur, null will be returned + * + * @return the creator of this view in form of an Address object + */ + public Address getCreator() { + return vid != null ? vid.getCoordAddress() : null; + } + + /** + * Returns a reference to the List of members (ordered) + * Do NOT change this list, hence your will invalidate the view + * Make a copy if you have to modify it. + * + * @return a reference to the ordered list of members in this view + */ + public Vector
getMembers() { + return Util.unmodifiableVector(members); + } + + /** + * returns true, if this view contains a certain member + * + * @param mbr - the address of the member, + * @return true if this view contains the member, false if it doesn't + * if the argument mbr is null, this operation returns false + */ + public boolean containsMember(Address mbr) { + return !(mbr == null || members == null) && members.contains(mbr); + } + + + public boolean equals(Object obj) { + if(!(obj instanceof View)) + return false; + if(vid != null) { + int rc=vid.compareTo(((View)obj).vid); + if(rc != 0) + return false; + if(members != null && ((View)obj).members != null) { + return members.equals(((View)obj).members); + } + } + else { + if(((View)obj).vid == null) + return true; + } + return false; + } + + + public int hashCode() { + return vid != null? vid.hashCode() : 0; + } + + /** + * returns the number of members in this view + * + * @return the number of members in this view 0..n + */ + public int size() { + return members == null ? 0 : members.size(); + } + + + /** + * creates a copy of this view + * + * @return a copy of this view + */ + public Object clone() { + ViewId vid2=vid != null ? (ViewId)vid.clone() : null; + Vector
members2=members != null ? new Vector
(members) : null; + return new View(vid2, members2); + } + + + /** + * debug only + */ + public String printDetails() { + StringBuilder ret=new StringBuilder(); + ret.append(vid).append("\n\t"); + if(members != null) { + for(int i=0; i < members.size(); i++) { + ret.append(members.elementAt(i)).append("\n\t"); + } + ret.append('\n'); + } + return ret.toString(); + } + + /** + * Adds a key and value to the view. Since the payloads will be shipped around *with* the view, so the keys and + * values need to be serializable. Note that the total serialized size of all keys and values cannot + * exceed 65000 bytes ! + * @param key + * @param value + */ + public void addPayload(String key, Object value) { + if(payload == null) { + payload=new HashMap(7); + } + payload.put(key, value); + } + + public Object getPayload(String key) { + if(payload != null) + return payload.get(key); + return null; + } + + + public String toString() { + StringBuilder ret=new StringBuilder(64); + ret.append(vid).append(" ").append(members); + return ret.toString(); + } + + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeObject(vid); + out.writeObject(members); + if(payload != null && !payload.isEmpty()) { + out.writeBoolean(true); + out.writeObject(payload); + } + else { + out.writeBoolean(false); + } + } + + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + vid=(ViewId)in.readObject(); + members=(Vector
)in.readObject(); + if(in.readBoolean()) { + payload=(Map)in.readObject(); + } + } + + + public void writeTo(DataOutputStream out) throws IOException { + // vid + if(vid != null) { + out.writeBoolean(true); + vid.writeTo(out); + } + else + out.writeBoolean(false); + + // members: + Util.writeAddresses(members, out); + + if(payload != null && !payload.isEmpty()) { + try { + byte buffer[]=Util.objectToByteBuffer(payload); + out.writeShort(buffer.length); + out.write(buffer, 0, buffer.length); + } + catch(Exception e) { + throw new IOException("could not write View payload"); + } + } + else { + out.writeShort(0); + } + } + + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + boolean b; + // vid: + b=in.readBoolean(); + if(b) { + vid=new ViewId(); + vid.readFrom(in); + } + + // members: + members=(Vector
)Util.readAddresses(in, Vector.class); + + short payloadLength=in.readShort(); + if(payloadLength > 0) { + byte[] buffer=new byte[payloadLength]; + in.read(buffer); + try { + payload=(Map)Util.objectFromByteBuffer(buffer); + } + catch(Exception e) { + throw new IOException("Could not read View payload " + buffer.length); + } + } + } + + public int serializedSize() { + int retval=Global.BYTE_SIZE; // presence for vid + if(vid != null) + retval+=vid.serializedSize(); + retval+=Util.size(members); + + retval+=Global.SHORT_SIZE; // presence for payload + if(payload != null) { + retval+=Util.sizeOf(payload); + } + return retval; + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/ViewId.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/ViewId.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/ViewId.java 17 Aug 2012 14:51:05 -0000 1.1 @@ -0,0 +1,144 @@ +// $Id: ViewId.java,v 1.1 2012/08/17 14:51:05 marcin Exp $ + +package org.jgroups; + +import org.jgroups.util.Streamable; +import org.jgroups.util.Util; + +import java.io.*; + + +/** + * ViewIds are used for ordering views (each view has a ViewId and a list of members). + * Ordering between views is important for example in a virtual synchrony protocol where + * all views seen by a member have to be ordered. + */ +public class ViewId implements Externalizable, Comparable, Cloneable, Streamable { + Address coord_addr=null; // Address of the issuer of this view + long id=0; // Lamport time of the view + + + public ViewId() { // used for externalization + } + + + /** + * Creates a ViewID with the coordinator address and a Lamport timestamp of 0. + * + * @param coord_addr the address of the member that issued this view + */ + public ViewId(Address coord_addr) { + this.coord_addr=coord_addr; + } + + /** + * Creates a ViewID with the coordinator address and the given Lamport timestamp. + * + * @param coord_addr - the address of the member that issued this view + * @param id - the Lamport timestamp of the view + */ + public ViewId(Address coord_addr, long id) { + this.coord_addr=coord_addr; + this.id=id; + } + + /** + * returns the lamport time of the view + * + * @return the lamport time timestamp + */ + public long getId() { + return id; + } + + + /** + * returns the address of the member that issued this view + * + * @return the Address of the the issuer + */ + public Address getCoordAddress() { + return coord_addr; + } + + + public String toString() { + return "[" + coord_addr + '|' + id + ']'; + } + + /** + * Cloneable interface + * Returns a new ViewID object containing the same address and lamport timestamp as this view + */ + public Object clone() { + return new ViewId(coord_addr, id); + } + + /** + * Old Copy method, deprecated because it is substituted by clone() + */ + public ViewId copy() { + return (ViewId)clone(); + } + + /** + * Establishes an order between 2 ViewIds. First compare on id. Compare on coord_addr + * only if necessary (i.e. ids are equal) ! + * + * @return 0 for equality, value less than 0 if smaller, greater than 0 if greater. + */ + public int compareTo(Object other) { + if(other == null) return 1; //+++ Maybe necessary to throw an exception + + if(!(other instanceof ViewId)) { + throw new ClassCastException("ViewId.compareTo(): view id is not comparable with different Objects"); + } + return id > ((ViewId)other).id ? 1 : id < ((ViewId)other).id ? -1 : 0; + } + + /** + * Old Compare + */ + public int compare(Object o) { + return compareTo(o); + } + + + public boolean equals(Object other_view) { + return compareTo(other_view) == 0; + } + + + public int hashCode() { + return (int)id; + } + + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeObject(coord_addr); + out.writeLong(id); + } + + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + coord_addr=(Address)in.readObject(); + id=in.readLong(); + } + + public void writeTo(DataOutputStream out) throws IOException { + Util.writeAddress(coord_addr, out); + out.writeLong(id); + } + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + coord_addr=Util.readAddress(in); + id=in.readLong(); + } + + public int serializedSize() { + int retval=Global.LONG_SIZE; // for the id + retval+=Util.size(coord_addr); + return retval; + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/overview.html =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/overview.html,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/overview.html 17 Aug 2012 14:51:05 -0000 1.1 @@ -0,0 +1,15 @@ + + + +JGroups is a toolkit for reliable group communication. Processes +can join a group, send messages to all members or single members, and +receive messages from members in the group. The system keeps track of +the members in every group, and notifies group members when a new +member joins, or an existing member leaves or crashes. A group is +identified by its name. Groups do not have to be created explicitly; +when a process joins a non-existing group, that group will be created +automatically. Member processes of a group can be located on the same +host, within the same LAN, or across a WAN. A member can be part of +multiple groups. + + \ No newline at end of file Index: 3rdParty_sources/jgroups/org/jgroups/package.html =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/package.html,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/package.html 17 Aug 2012 14:51:03 -0000 1.1 @@ -0,0 +1,5 @@ + + + Provides top-level public JGroups classes such as Channel, Message, etc. + + \ No newline at end of file Index: 3rdParty_sources/jgroups/org/jgroups/annotations/GuardedBy.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/annotations/GuardedBy.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/annotations/GuardedBy.java 17 Aug 2012 14:51:28 -0000 1.1 @@ -0,0 +1,23 @@ +package org.jgroups.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Copyright (c) 2005 Brian Goetz and Tim Peierls + * Released under the Creative Commons Attribution License + * (http://creativecommons.org/licenses/by/2.5) + * Official home: http://www.jcip.net + * + * Adopted from Java Concurrency in Practice. This annotation defines the monitor that protects the variable + * annotated by @GuardedBy, e.g. @GuardedBy("lock") or @GuardedBy("this") + * @author Bela Ban + * @version $Id: GuardedBy.java,v 1.1 2012/08/17 14:51:28 marcin Exp $ + */ +@Target({ElementType.FIELD, ElementType.METHOD}) +@Retention(RetentionPolicy.SOURCE) +public @interface GuardedBy { + String value(); +} Index: 3rdParty_sources/jgroups/org/jgroups/annotations/Immutable.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/annotations/Immutable.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/annotations/Immutable.java 17 Aug 2012 14:51:28 -0000 1.1 @@ -0,0 +1,22 @@ +package org.jgroups.annotations; + +import java.lang.annotation.Target; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Copyright (c) 2005 Brian Goetz and Tim Peierls + * Released under the Creative Commons Attribution License + * (http://creativecommons.org/licenses/by/2.5) + * Official home: http://www.jcip.net + * + * Adopted from Java Concurrency in Practice. This annotation defines an immutable class, ie. a class whose + * instances cannot be modified after creation + * @author Bela Ban + * @version $Id: Immutable.java,v 1.1 2012/08/17 14:51:28 marcin Exp $ + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.SOURCE) +public @interface Immutable { +} Index: 3rdParty_sources/jgroups/org/jgroups/auth/AuthToken.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/auth/AuthToken.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/auth/AuthToken.java 17 Aug 2012 14:51:28 -0000 1.1 @@ -0,0 +1,33 @@ +package org.jgroups.auth; + +import org.jgroups.util.Streamable; +import org.jgroups.Message; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.Serializable; +import java.util.Properties; +/** + * Abstract AuthToken class used by implementations of AUTH, e.g. SimpleToken, X509Token + * @author Chris Mills + */ +public abstract class AuthToken implements Serializable, Streamable{ + protected final Log log = LogFactory.getLog(this.getClass()); + /** + * Used to return the full package and class name of the implementation. This is used by the AUTH protocol to create an instance of the implementation. + * @return a java.lang.String object of the package and class name + */ + public abstract String getName(); + /** + * Called during the setup of the AUTH protocol to pass property values from the JGroups config XML document to the implementing class. + * @param properties a java.util.Properties object of config parameters + */ + public abstract void setValue(Properties properties); + /** + * This method should be implemented to perform the actual authentication of joining members. + * @param token the token sent by the joiner + * @param msg the Message object containing the actual JOIN_REQ + * @return true if authenticaion passed or false if it failed. + */ + public abstract boolean authenticate(AuthToken token, Message msg); +} Index: 3rdParty_sources/jgroups/org/jgroups/auth/FixedMembershipToken.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/auth/FixedMembershipToken.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/auth/FixedMembershipToken.java 17 Aug 2012 14:51:28 -0000 1.1 @@ -0,0 +1,128 @@ +/* +* JBoss, Home of Professional Open Source +* Copyright 2005, JBoss Inc., and individual contributors as indicated +* by the @authors tag. See the copyright.txt in the distribution for a +* full listing of individual contributors. +* +* This is free software; you can redistribute it and/or modify it +* under the terms of the GNU Lesser General Public License as +* published by the Free Software Foundation; either version 2.1 of +* the License, or (at your option) any later version. +* +* This software is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this software; if not, write to the Free +* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +* 02110-1301 USA, or see the FSF site: http://www.fsf.org. +*/ +package org.jgroups.auth; + +import org.jgroups.Message; +import org.jgroups.util.Util; + +import java.util.Properties; +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.DataInputStream; + +/** + *

+ * The FixedMemberShipToken object predefines a list of IP addresses and Ports that can join the group. + *

+ *

+ * Configuration parameters for this example are shown below: + *

+ *
    + *
  • fixed_members_value (required) = List of IP addresses & ports (optionally) - ports must be seperated by a '/' e.g. 127.0.0.1/1010*127.0.0.1/4567
  • + *
  • fixed_members_seperator (required) = The seperator used between IP addresses - e.g. *
  • + *
+ * @author Chris Mills (millsy@jboss.com) + */ +public class FixedMembershipToken extends AuthToken { + private static final String FIXED_MEMBERS_ATTR = "fixed_members_value"; + private static final String FIXED_MEMBERS_SEPERATOR_ATTR = "fixed_members_seperator"; + + private List memberList = null; + private String token = "emptyToken"; + + public FixedMembershipToken(){ + } + + public String getName(){ + return "org.jgroups.auth.FixedMembershipToken"; + } + + public boolean authenticate(AuthToken token, Message msg){ + if((token != null) && (token instanceof FixedMembershipToken) && (this.memberList != null)){ + //Found a valid Token to authenticate against + FixedMembershipToken serverToken = (FixedMembershipToken) token; + + String sourceAddressWithPort = msg.getSrc().toString(); + String sourceAddressWithoutPort = sourceAddressWithPort.substring(0, sourceAddressWithPort.indexOf(":")); + + if(log.isDebugEnabled()){ + log.debug("AUTHToken received from " + sourceAddressWithPort); + } + + if((this.memberList.contains(sourceAddressWithPort)) || (this.memberList.contains(sourceAddressWithoutPort))){ + //validated + if(log.isDebugEnabled()){ + log.debug("FixedMembershipToken match"); + } + return true; + }else{ + if(log.isWarnEnabled()){ + log.warn("Authentication failed on FixedMembershipToken"); + } + return false; + } + } + + if(log.isWarnEnabled()){ + log.warn("Invalid AuthToken instance - wrong type or null"); + } + return false; + } + + public void setValue(Properties properties){ + memberList = new ArrayList(); + StringTokenizer memberListTokenizer = new StringTokenizer((String)properties.get(FixedMembershipToken.FIXED_MEMBERS_ATTR), + (String)properties.get(FixedMembershipToken.FIXED_MEMBERS_SEPERATOR_ATTR)); + while(memberListTokenizer.hasMoreTokens()){ + memberList.add(memberListTokenizer.nextToken().replace('/', ':')); + } + properties.remove(FixedMembershipToken.FIXED_MEMBERS_ATTR); + properties.remove(FixedMembershipToken.FIXED_MEMBERS_SEPERATOR_ATTR); + } + /** + * Required to serialize the object to pass across the wire + * @param out + * @throws java.io.IOException + */ + public void writeTo(DataOutputStream out) throws IOException { + if(log.isDebugEnabled()){ + log.debug("SimpleToken writeTo()"); + } + Util.writeString(this.token, out); + } + /** + * Required to deserialize the object when read in from the wire + * @param in + * @throws IOException + * @throws IllegalAccessException + * @throws InstantiationException + */ + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + if(log.isDebugEnabled()){ + log.debug("SimpleToken readFrom()"); + } + this.token = Util.readString(in); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/auth/MD5Token.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/auth/MD5Token.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/auth/MD5Token.java 17 Aug 2012 14:51:28 -0000 1.1 @@ -0,0 +1,123 @@ +package org.jgroups.auth; + +import org.jgroups.Message; +import org.jgroups.util.Util; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Properties; + +/** + *

+ * This is an example of using a preshared token that is encrypted using an MD5/SHA hash for authentication purposes. All members of the group have to have the same string value in the JGroups config. + *

+ *

+ * Configuration parameters for this example are shown below: + *

+ *
    + *
  • token_hash (required) = MD5(default)/SHA
  • + *
  • auth_value (required) = the string to encrypt
  • + *
+ * @see org.jgroups.auth.AuthToken + * @author Chris Mills + */ +public class MD5Token extends AuthToken { + + public static final String TOKEN_ATTR = "auth_value"; + public static final String TOKEN_TYPE = "token_hash"; + + private String token = null; + private String hash_type = "MD5"; + + public MD5Token(){ + //need an empty constructor + } + + public MD5Token(String token){ + this.token = hash(token); + } + + public MD5Token(String token, String hash_type){ + this.token = hash(token); + this.hash_type = hash_type; + } + + public void setValue(Properties properties){ + this.token = hash((String)properties.get(MD5Token.TOKEN_ATTR)); + properties.remove(MD5Token.TOKEN_ATTR); + + if(properties.containsKey(MD5Token.TOKEN_TYPE)){ + hash_type = (String)properties.get(MD5Token.TOKEN_TYPE); + properties.remove(MD5Token.TOKEN_TYPE); + } + } + + public String getName(){ + return "org.jgroups.auth.MD5Token"; + } + /** + * Called during setup to hash the auth_value string in to an MD5/SHA hash + * @param token the string to hash + * @return the hashed version of the string + */ + private String hash(String token){ + //perform the hashing of the token key + String hashedToken = null; + + if(hash_type.equalsIgnoreCase("SHA")){ + hashedToken = Util.sha(token); + }else{ + hashedToken = Util.md5(token); + } + + if(hashedToken == null){ + //failed to encrypt + if(log.isWarnEnabled()){ + log.warn("Failed to hash token - sending in clear text"); + } + return token; + } + return hashedToken; + } + + public boolean authenticate(AuthToken token, Message msg){ + + if((token != null) && (token instanceof MD5Token)){ + //Found a valid Token to authenticate against + MD5Token serverToken = (MD5Token) token; + + if((this.token != null) && (serverToken.token != null) && (this.token.equalsIgnoreCase(serverToken.token))){ + //validated + if(log.isDebugEnabled()){ + log.debug("MD5Token match"); + } + return true; + }else{ + if(log.isWarnEnabled()){ + log.warn("Authentication failed on MD5Token"); + } + return false; + } + } + + if(log.isWarnEnabled()){ + log.warn("Invalid AuthToken instance - wrong type or null"); + } + return false; + } + + public void writeTo(DataOutputStream out) throws IOException { + if(log.isDebugEnabled()){ + log.debug("MD5Token writeTo()"); + } + Util.writeString(this.token, out); + } + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + if(log.isDebugEnabled()){ + log.debug("MD5Token readFrom()"); + } + this.token = Util.readString(in); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/auth/SimpleToken.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/auth/SimpleToken.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/auth/SimpleToken.java 17 Aug 2012 14:51:28 -0000 1.1 @@ -0,0 +1,91 @@ +package org.jgroups.auth; + +import org.jgroups.util.Util; +import org.jgroups.Message; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.DataInputStream; +import java.util.Properties; +/** + *

+ * This is an example of using a preshared token for authentication purposes. All members of the group have to have the same string value in the JGroups config. + *

+ *

JGroups config parameters:

+ *
    + *
  • auth_value (required) = the string to encrypt
  • + *
+ * @see org.jgroups.auth.AuthToken + * @author Chris Mills + */ +public class SimpleToken extends AuthToken { + + public static final String TOKEN_ATTR = "auth_value"; + private String token = null; + + public SimpleToken(){ + //need an empty constructor + } + + public SimpleToken(String token){ + this.token = token; + } + + public void setValue(Properties properties){ + this.token = (String)properties.get(SimpleToken.TOKEN_ATTR); + properties.remove(SimpleToken.TOKEN_ATTR); + } + + public String getName(){ + return "org.jgroups.auth.SimpleToken"; + } + + public boolean authenticate(AuthToken token, Message msg){ + if((token != null) && (token instanceof SimpleToken)){ + //Found a valid Token to authenticate against + SimpleToken serverToken = (SimpleToken) token; + + if((this.token != null) && (serverToken.token != null) && (this.token.equalsIgnoreCase(serverToken.token))){ + //validated + if(log.isDebugEnabled()){ + log.debug("SimpleToken match"); + } + return true; + }else{ + if(log.isWarnEnabled()){ + log.warn("Authentication failed on SimpleToken"); + } + return false; + } + } + + if(log.isWarnEnabled()){ + log.warn("Invalid AuthToken instance - wrong type or null"); + } + return false; + } + /** + * Required to serialize the object to pass across the wire + * @param out + * @throws IOException + */ + public void writeTo(DataOutputStream out) throws IOException { + if(log.isDebugEnabled()){ + log.debug("SimpleToken writeTo()"); + } + Util.writeString(this.token, out); + } + /** + * Required to deserialize the object when read in from the wire + * @param in + * @throws IOException + * @throws IllegalAccessException + * @throws InstantiationException + */ + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + if(log.isDebugEnabled()){ + log.debug("SimpleToken readFrom()"); + } + this.token = Util.readString(in); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/auth/X509Token.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/auth/X509Token.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/auth/X509Token.java 17 Aug 2012 14:51:28 -0000 1.1 @@ -0,0 +1,245 @@ +package org.jgroups.auth; + +import org.jgroups.util.Util; +import org.jgroups.Message; + +import javax.crypto.Cipher; +import java.io.*; +import java.util.Properties; +import java.security.cert.X509Certificate; +import java.security.PrivateKey; +import java.security.KeyStore; +/** + *

+ * This is an example of using a preshared token that is encrypted using an X509 certificate for authentication purposes. All members of the group have to have the same string value in the JGroups config. + *

+ *

+ * This example uses certificates contained within a specified keystore. Configuration parameters for this example are shown below: + *

+ *
    + *
  • keystore_type = JKS(default)/PKCS12 - see http://java.sun.com/j2se/1.4.2/docs/guide/security/CryptoSpec.html#AppA
  • + *
  • keystore_path (required) = the location of the keystore
  • + *
  • keystore_password (required) = the password of the keystore
  • + *
  • cert_alias (required) = the alias of the certification within the keystore
  • + *
  • cert_password = the password of the certification within the keystore
  • + *
  • auth_value (required) = the string to encrypt
  • + *
  • cipher_type = RSA(default)/AES/Blowfish/DES/DESede/PBEWithMD5AndDES/PBEWithHmacSHA1AndDESede/RC2/RC4/RC5 - see http://java.sun.com/j2se/1.4.2/docs/guide/security/jce/JCERefGuide.html#AppA
  • + *
+ * @see org.jgroups.auth.AuthToken + * @author Chris Mills + */ +public class X509Token extends AuthToken { + + public static final String KEYSTORE_TYPE = "keystore_type"; + public static final String KEYSTORE_PATH = "keystore_path"; + public static final String KEYSTORE_PASSWORD = "keystore_password"; + public static final String CERT_ALIAS = "cert_alias"; + public static final String CERT_PASSWORD = "cert_password"; + public static final String TOKEN_ATTR = "auth_value"; + public static final String CIPHER_TYPE = "cipher_type"; + + private boolean valueSet = false; + + private String keystore_type = null; + private String cert_alias = null; + private String keystore_path = null; + private String token_attr = null; + private String cipher_type = null; + + private byte[] encryptedToken = null; + + private char[] cert_password = null; + private char[] keystore_password = null; + + private Cipher cipher = null; + private PrivateKey certPrivateKey = null; + private X509Certificate certificate = null; + + public X509Token() { + //need an empty constructor + } + + public void setValue(Properties properties) { + if(log.isDebugEnabled()){ + log.debug("setting values on X509Token object"); + } + + if(properties.containsKey(TOKEN_ATTR)){ + this.token_attr = (String) properties.get(TOKEN_ATTR); + properties.remove(TOKEN_ATTR); + if(log.isDebugEnabled()){ + log.debug("token_attr = " + this.token_attr); + } + } + + if(properties.containsKey(KEYSTORE_TYPE)){ + this.keystore_type = (String) properties.get(KEYSTORE_TYPE); + properties.remove(KEYSTORE_TYPE); + if(log.isDebugEnabled()){ + log.debug("keystore_type = " + this.keystore_type); + } + }else{ + this.keystore_type = "JKS"; + if(log.isDebugEnabled()){ + log.debug("keystore_type = " + this.keystore_type); + } + } + + if(properties.containsKey(KEYSTORE_PATH)){ + this.keystore_path = (String) properties.get(KEYSTORE_PATH); + properties.remove(KEYSTORE_PATH); + if(log.isDebugEnabled()){ + log.debug("keystore_path = " + this.keystore_path); + } + } + + if(properties.containsKey(KEYSTORE_PASSWORD)){ + this.keystore_password = ((String) properties.get(KEYSTORE_PASSWORD)).toCharArray(); + properties.remove(KEYSTORE_PASSWORD); + if(log.isDebugEnabled()){ + log.debug("keystore_password = " + this.keystore_password); + } + } + + if(properties.containsKey(CERT_ALIAS)){ + this.cert_alias = (String) properties.get(CERT_ALIAS); + properties.remove(CERT_ALIAS); + if(log.isDebugEnabled()){ + log.debug("cert_alias = " + this.cert_alias); + } + } + + if(properties.containsKey(CERT_PASSWORD)){ + this.cert_password = ((String) properties.get(CERT_PASSWORD)).toCharArray(); + properties.remove(CERT_PASSWORD); + if(log.isDebugEnabled()){ + log.debug("cert_password = " + this.cert_password); + } + }else{ + this.cert_password = this.keystore_password; + if(log.isDebugEnabled()){ + log.debug("cert_password = " + this.cert_password); + } + } + + if(properties.containsKey(CIPHER_TYPE)){ + this.cipher_type = (String) properties.get(CIPHER_TYPE); + properties.remove(CIPHER_TYPE); + if(log.isDebugEnabled()){ + log.debug("cipher_type = " + this.cipher_type); + } + }else{ + this.cipher_type = "RSA"; + if(log.isDebugEnabled()){ + log.debug("cipher_type = " + this.cipher_type); + } + } + + if(getCertificate()){ + this.valueSet = true; + if(log.isDebugEnabled()){ + log.debug("X509Token created correctly"); + } + } + } + + public String getName() { + return "org.jgroups.auth.X509Token"; + } + + public boolean authenticate(AuthToken token, Message msg) { + if (!this.valueSet) { + if(log.isFatalEnabled()){ + log.fatal("X509Token not setup correctly - check token attrs"); + } + return false; + } + + if((token != null) && (token instanceof X509Token)){ + //got a valid X509 token object + X509Token serverToken = (X509Token)token; + if(!serverToken.valueSet){ + if(log.isFatalEnabled()){ + log.fatal("X509Token - recieved token not valid"); + } + return false; + } + + try{ + if(log.isDebugEnabled()){ + log.debug("setting cipher to decrypt mode"); + } + this.cipher.init(Cipher.DECRYPT_MODE, this.certPrivateKey); + String serverBytes = new String(this.cipher.doFinal(serverToken.encryptedToken)); + if((serverBytes != null) && (serverBytes.equalsIgnoreCase(this.token_attr))){ + if(log.isDebugEnabled()){ + log.debug("X509 authentication passed"); + } + return true; + } + }catch(Exception e){ + if(log.isFatalEnabled()){ + log.fatal(e); + } + } + } + if(log.isWarnEnabled()){ + log.warn("X509 authentication failed"); + } + return false; + } + + public void writeTo(DataOutputStream out) throws IOException { + if(log.isDebugEnabled()){ + log.debug("X509Token writeTo()"); + } + Util.writeByteBuffer(this.encryptedToken, out); + } + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + if(log.isDebugEnabled()){ + log.debug("X509Token readFrom()"); + } + this.encryptedToken = Util.readByteBuffer(in); + this.valueSet = true; + } + /** + * Used during setup to get the certification from the keystore and encrypt the auth_value with the private key + * @return true if the certificate was found and the string encypted correctly otherwise returns false + */ + private boolean getCertificate() { + try{ + KeyStore store = KeyStore.getInstance(this.keystore_type); + java.io.FileInputStream fis = new java.io.FileInputStream(this.keystore_path); + store.load(fis, this.keystore_password); + + this.cipher = Cipher.getInstance(this.cipher_type); + this.certificate = (X509Certificate) store.getCertificate(this.cert_alias); + + if(log.isDebugEnabled()){ + log.debug("certificate = " + this.certificate.toString()); + } + + this.cipher.init(Cipher.ENCRYPT_MODE, this.certificate); + this.encryptedToken = this.cipher.doFinal(this.token_attr.getBytes()); + + if(log.isDebugEnabled()){ + log.debug("encryptedToken = " + this.encryptedToken); + } + + KeyStore.PrivateKeyEntry privateKey = (KeyStore.PrivateKeyEntry)store.getEntry(this.cert_alias, new KeyStore.PasswordProtection(this.cert_password)); + this.certPrivateKey = privateKey.getPrivateKey(); + + if(log.isDebugEnabled()){ + log.debug("certPrivateKey = " + this.certPrivateKey.toString()); + } + + return true; + }catch(Exception e){ + if(log.isFatalEnabled()){ + log.fatal(e); + } + return false; + } + } +} Index: 3rdParty_sources/jgroups/org/jgroups/blocks/BasicConnectionTable.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/blocks/BasicConnectionTable.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/blocks/BasicConnectionTable.java 17 Aug 2012 14:51:18 -0000 1.1 @@ -0,0 +1,853 @@ +package org.jgroups.blocks; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.Address; +import org.jgroups.Global; +import org.jgroups.Version; +import org.jgroups.stack.IpAddress; +import org.jgroups.util.DefaultThreadFactory; +import org.jgroups.util.PortsManager; +import org.jgroups.util.ThreadFactory; +import org.jgroups.util.Util; + +import java.io.*; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; +import java.util.*; +import java.util.Map.Entry; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +/** + * Shared class for TCP connection tables. + * @author Scott Marlow + * @author Bela Ban + */ +public abstract class BasicConnectionTable { + private ThreadFactory factory; + final Map conns=new HashMap(); // keys: Addresses (peer address), values: Connection + Receiver receiver=null; + boolean use_send_queues=false; // max number of messages in a send queue + int send_queue_size=10000; + InetAddress bind_addr=null; + Address local_addr=null; // bind_addr + port of srv_sock + int srv_port=7800; + int recv_buf_size=120000; + int send_buf_size=60000; + final Vector conn_listeners=new Vector(); // listeners to be notified when a conn is established/torn down + Reaper reaper=null; // closes conns that have been idle for more than n secs + long reaper_interval=60000; // reap unused conns once a minute + long conn_expire_time=300000; // connections can be idle for 5 minutes before they are reaped + int sock_conn_timeout=1000; // max time in millis to wait for Socket.connect() to return + int peer_addr_read_timeout=2000; // max time in milliseconds to block on reading peer address + final ThreadGroup thread_group=new ThreadGroup(Util.getGlobalThreadGroup(), "ConnectionTable"); + protected final Log log= LogFactory.getLog(getClass()); + final byte[] cookie={'b', 'e', 'l', 'a'}; + boolean use_reaper=false; // by default we don't reap idle conns + static final int backlog=20; // 20 conn requests are queued by ServerSocket (addtl will be discarded) + volatile ServerSocket srv_sock=null; + boolean tcp_nodelay=false; + int linger=-1; + + protected PortsManager pm=null; + + /** + * The address which will be broadcast to the group (the externally visible address which this host should + * be contacted on). If external_addr is null, it will default to the same address that the server socket is bound to. + */ + InetAddress external_addr=null; + int max_port=0; // maximum port to bind to (if < srv_port, no limit) + Thread acceptor=null; // continuously calls srv_sock.accept() + boolean running=false; + /** Total number of Connections created for this connection table */ + static AtomicInteger conn_creations=new AtomicInteger(0); + + final static long MAX_JOIN_TIMEOUT=Global.THREAD_SHUTDOWN_WAIT_TIME; + + protected BasicConnectionTable() { + factory = new DefaultThreadFactory(new ThreadGroup(Util.getGlobalThreadGroup(),"ConnectionTable"),"Connection Table", false); + } + + public final void setReceiver(Receiver r) { + receiver=r; + } + + public void addConnectionListener(ConnectionListener l) { + if(l != null && !conn_listeners.contains(l)) + conn_listeners.addElement(l); + } + + public void removeConnectionListener(ConnectionListener l) { + if(l != null) conn_listeners.removeElement(l); + } + + public Address getLocalAddress() { + if(local_addr == null) + local_addr=bind_addr != null ? new IpAddress(bind_addr, srv_port) : null; + return local_addr; + } + + public int getSendBufferSize() { + return send_buf_size; + } + + public void setSendBufferSize(int send_buf_size) { + this.send_buf_size=send_buf_size; + } + + public int getReceiveBufferSize() { + return recv_buf_size; + } + + public void setReceiveBufferSize(int recv_buf_size) { + this.recv_buf_size=recv_buf_size; + } + + public int getSocketConnectionTimeout() { + return sock_conn_timeout; + } + + public void setSocketConnectionTimeout(int sock_conn_timeout) { + this.sock_conn_timeout=sock_conn_timeout; + } + + public int getPeerAddressReadTimeout() { + return peer_addr_read_timeout; + } + + public void setPeerAddressReadTimeout(int peer_addr_read_timeout) { + this.peer_addr_read_timeout=peer_addr_read_timeout; + } + + public int getNumConnections() { + return conns.size(); + } + + public static int getNumberOfConnectionCreations() { + return conn_creations.intValue(); + } + + public boolean getTcpNodelay() { + return tcp_nodelay; + } + + public void setTcpNodelay(boolean tcp_nodelay) { + this.tcp_nodelay=tcp_nodelay; + } + + public int getLinger() { + return linger; + } + + public void setLinger(int linger) { + this.linger=linger; + } + + public void setThreadFactory(ThreadFactory factory){ + this.factory = factory; + } + + public ThreadFactory getThreadFactory(){ + return factory; + } + + public boolean getUseSendQueues() {return use_send_queues;} + + public void setUseSendQueues(boolean flag) {this.use_send_queues=flag;} + + public int getSendQueueSize() { + return send_queue_size; + } + + public void setSendQueueSize(int send_queue_size) { + this.send_queue_size=send_queue_size; + } + + public void start() throws Exception { + running=true; + } + + public void stop() { + running=false; + + // 1. Stop the reaper + if(reaper != null) + reaper.stop(); + + // 2. close the server socket (this also stops the acceptor thread) + if(srv_sock != null) { + try { + if(pm != null) { + int tmp_port=srv_sock.getLocalPort(); + pm.updatePort(tmp_port); + } + ServerSocket tmp=srv_sock; + srv_sock=null; + tmp.close(); + if(acceptor != null) + Util.interruptAndWaitToDie(acceptor); + } + catch(Exception e) { + } + } + + // 3. then close the connections + Collection connsCopy=null; + synchronized(conns) { + connsCopy=new LinkedList(conns.values()); + conns.clear(); + } + for(Connection conn:connsCopy) { + conn.destroy(); + } + connsCopy.clear(); + local_addr=null; + } + + /** + Remove addrfrom connection table. This is typically triggered when a member is suspected. + */ + public void removeConnection(Address addr) { + Connection conn; + + synchronized(conns) { + conn=conns.remove(addr); + } + + if(conn != null) { + try { + conn.destroy(); // won't do anything if already destroyed + } + catch(Exception e) { + } + } + if(log.isTraceEnabled()) log.trace("removed " + addr + ", connections are " + toString()); + } + + /** + * Calls the receiver callback. We do not serialize access to this method, and it may be called concurrently + * by several Connection handler threads. Therefore the receiver needs to be reentrant. + */ + public void receive(Address sender, byte[] data, int offset, int length) { + if(receiver != null) { + receiver.receive(sender, data, offset, length); + } + else + if(log.isErrorEnabled()) log.error("receiver is null (not set) !"); + } + + public String toString() { + StringBuilder ret=new StringBuilder(); + Address key; + Connection val; + Entry entry; + HashMap copy; + + synchronized(conns) { + copy=new HashMap(conns); + } + ret.append("local_addr=" + local_addr).append("\n"); + ret.append("connections (" + copy.size() + "):\n"); + for(Iterator> it=copy.entrySet().iterator(); it.hasNext();) { + entry=it.next(); + key=entry.getKey(); + val=entry.getValue(); + ret.append("key: " + key + ": " + val + '\n'); + } + ret.append('\n'); + return ret.toString(); + } + + void notifyConnectionOpened(Address peer) { + if(peer == null) return; + for(int i=0; i < conn_listeners.size(); i++) + conn_listeners.elementAt(i).connectionOpened(peer); + } + + void notifyConnectionClosed(Address peer) { + if(peer == null) return; + for(int i=0; i < conn_listeners.size(); i++) + conn_listeners.elementAt(i).connectionClosed(peer); + } + + void addConnection(Address peer, Connection c) { + synchronized (conns) { + conns.put(peer, c); + } + if(reaper != null && !reaper.isRunning()) + reaper.start(); + } + + public void send(Address dest, byte[] data, int offset, int length) throws Exception { + Connection conn; + if(dest == null) { + if(log.isErrorEnabled()) + log.error("destination is null"); + return; + } + + if(data == null) { + log.warn("data is null; discarding packet"); + return; + } + + if(!running) { + if(log.isWarnEnabled()) + log.warn("connection table is not running, discarding message to " + dest); + return; + } + + if(dest.equals(local_addr)) { + receive(local_addr, data, offset, length); + return; + } + + // 1. Try to obtain correct Connection (or create one if not yet existent) + try { + conn=getConnection(dest); + if(conn == null) return; + } + catch(Throwable ex) { + throw new Exception("connection to " + dest + " could not be established", ex); + } + + // 2. Send the message using that connection + try { + conn.send(data, offset, length); + } + catch(Throwable ex) { + if(log.isTraceEnabled()) + log.trace("sending msg to " + dest + " failed (" + ex.getClass().getName() + "); removing from connection table", ex); + removeConnection(dest); + } + } + + abstract Connection getConnection(Address dest) throws Exception; + + /** + * Removes all connections from ConnectionTable which are not in current_mbrs + * @param current_mbrs + */ + public void retainAll(Collection
current_mbrs) { + if(current_mbrs == null) return; + HashMap copy; + synchronized(conns) { + copy=new HashMap(conns); + conns.keySet().retainAll(current_mbrs); + } + copy.keySet().removeAll(current_mbrs); + + //destroy orphaned connection i.e. connections + //to members that are not in current view + for(Connection orphanConnection:copy.values()){ + if (log.isTraceEnabled()) + log.trace("At " + local_addr + " destroying orphan to " + + orphanConnection.getPeerAddress()); + orphanConnection.destroy(); + } + copy.clear(); + } + + + + /** Used for message reception. */ + public interface Receiver { + void receive(Address sender, byte[] data, int offset, int length); + } + + /** Used to be notified about connection establishment and teardown. */ + public interface ConnectionListener { + void connectionOpened(Address peer_addr); + void connectionClosed(Address peer_addr); + } + + class Connection implements Runnable { + Socket sock=null; // socket to/from peer (result of srv_sock.accept() or new Socket()) + String sock_addr=null; // used for Thread.getName() + DataOutputStream out=null; // for sending messages + DataInputStream in=null; // for receiving messages + Thread receiverThread=null; // thread for receiving messages + Address peer_addr=null; // address of the 'other end' of the connection + final Lock send_lock=new ReentrantLock(); // serialize send() + long last_access=System.currentTimeMillis(); // last time a message was sent or received + + /** Bounded queue of data to be sent to the peer of this connection */ + BlockingQueue send_queue=null; + Sender sender=null; + boolean is_running=false; + + + private String getSockAddress() { + if(sock_addr != null) + return sock_addr; + if(sock != null) { + StringBuilder sb; + sb=new StringBuilder(); + sb.append(sock.getLocalAddress().getHostAddress()).append(':').append(sock.getLocalPort()); + sb.append(" - ").append(sock.getInetAddress().getHostAddress()).append(':').append(sock.getPort()); + sock_addr=sb.toString(); + } + return sock_addr; + } + + + + + Connection(Socket s, Address peer_addr) { + sock=s; + this.peer_addr=peer_addr; + + if(use_send_queues) { + send_queue=new LinkedBlockingQueue(send_queue_size); + sender=new Sender(); + } + + try { + // out=new DataOutputStream(sock.getOutputStream()); + // in=new DataInputStream(sock.getInputStream()); + + // The change to buffered input and output stream yielded a 400% performance gain ! + // bela Sept 7 2006 + out=new DataOutputStream(new BufferedOutputStream(sock.getOutputStream())); + in=new DataInputStream(new BufferedInputStream(sock.getInputStream())); + if(sender != null) + sender.start(); + conn_creations.incrementAndGet(); + } + catch(Exception ex) { + if(log.isErrorEnabled()) log.error("exception is " + ex); + } + } + + + boolean established() { + return receiverThread != null; + } + + + void setPeerAddress(Address peer_addr) { + this.peer_addr=peer_addr; + } + + Address getPeerAddress() {return peer_addr;} + + void updateLastAccessed() { + last_access=System.currentTimeMillis(); + } + + void init() { + is_running=true; + if(receiverThread == null || !receiverThread.isAlive()) { + // Roland Kurmann 4/7/2003, put in thread_group + receiverThread=getThreadFactory().newThread(thread_group,this, "ConnectionTable.Connection.Receiver [" + getSockAddress() + "]"); + receiverThread.start(); + if(log.isTraceEnabled()) + log.trace("receiver started: " + receiverThread); + } + + } + + /** + * Returns true if underlying socket to peer is closed + * + * @return + */ + boolean isSocketClosed() { + return !(sock != null && sock.isConnected()); + } + + + void destroy() { + if(log.isTraceEnabled()) log.trace("destroyed " + this); + is_running=false; + closeSocket(); // should terminate handler as well + if(sender != null) + sender.stop(); + Thread tmp=receiverThread; + receiverThread=null; + if(tmp != null) { + Util.interruptAndWaitToDie(tmp); + } + + conn_creations.decrementAndGet(); + } + + + /** + * + * @param data Guaranteed to be non null + * @param offset + * @param length + */ + void send(byte[] data, int offset, int length) { + if(!is_running) { + if(log.isWarnEnabled()) + log.warn("Connection is not running, discarding message"); + return; + } + if(use_send_queues) { + try { + // we need to copy the byte[] buffer here because the original buffer might get changed meanwhile + byte[] tmp=new byte[length]; + System.arraycopy(data, offset, tmp, 0, length); + send_queue.put(tmp); + } + catch(InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + else + _send(data, offset, length, true); + } + + + /** + * Sends data using the 'out' output stream of the socket + * @param data + * @param offset + * @param length + * @param acquire_lock + */ + private void _send(byte[] data, int offset, int length, boolean acquire_lock) { + if(acquire_lock) + send_lock.lock(); + + try { + doSend(data, offset, length); + updateLastAccessed(); + } + catch(InterruptedException iex) { + Thread.currentThread().interrupt(); // set interrupt flag again + } + catch(Throwable ex) { + if(log.isErrorEnabled()) log.error("failed sending data to " + peer_addr + ": " + ex); + } + finally { + if(acquire_lock) + send_lock.unlock(); + } + } + + + void doSend(byte[] data, int offset, int length) throws Exception { + try { + // we're using 'double-writes', sending the buffer to the destination in 2 pieces. this would + // ensure that, if the peer closed the connection while we were idle, we would get an exception. + // this won't happen if we use a single write (see Stevens, ch. 5.13). + if(out != null) { + out.writeInt(length); // write the length of the data buffer first + Util.doubleWrite(data, offset, length, out); + out.flush(); // may not be very efficient (but safe) + } + } + catch(Exception ex) { + removeConnection(peer_addr); + throw ex; + } + } + + + /** + * Reads the peer's address. First a cookie has to be sent which has to match my own cookie, otherwise + * the connection will be refused + */ + Address readPeerAddress(Socket client_sock) throws Exception { + Address client_peer_addr=null; + byte[] input_cookie=new byte[cookie.length]; + int client_port=client_sock != null? client_sock.getPort() : 0; + short version; + InetAddress client_addr=client_sock != null? client_sock.getInetAddress() : null; + + int timeout=client_sock.getSoTimeout(); + client_sock.setSoTimeout(peer_addr_read_timeout); + + try { + + if(in != null) { + initCookie(input_cookie); + + // read the cookie first + in.read(input_cookie, 0, input_cookie.length); + if(!matchCookie(input_cookie)) + throw new SocketException("ConnectionTable.Connection.readPeerAddress(): cookie sent by " + + client_peer_addr + " does not match own cookie; terminating connection"); + // then read the version + version=in.readShort(); + + if(Version.isBinaryCompatible(version) == false) { + if(log.isWarnEnabled()) + log.warn(new StringBuilder("packet from ").append(client_addr).append(':').append(client_port). + append(" has different version (").append(Version.print(version)).append(") from ours ("). + append(Version.printVersion()).append("). This may cause problems")); + } + client_peer_addr=new IpAddress(); + client_peer_addr.readFrom(in); + + updateLastAccessed(); + } + return client_peer_addr; + } + finally { + client_sock.setSoTimeout(timeout); + } + } + + + /** + * Send the cookie first, then the our port number. If the cookie doesn't match the receiver's cookie, + * the receiver will reject the connection and close it. + */ + void sendLocalAddress(Address local_addr) { + if(local_addr == null) { + if(log.isWarnEnabled()) log.warn("local_addr is null"); + return; + } + if(out != null) { + try { + // write the cookie + out.write(cookie, 0, cookie.length); + + // write the version + out.writeShort(Version.version); + local_addr.writeTo(out); + out.flush(); // needed ? + updateLastAccessed(); + } + catch(Throwable t) { + if(log.isErrorEnabled()) log.error("exception is " + t); + } + } + } + + + void initCookie(byte[] c) { + if(c != null) + for(int i=0; i < c.length; i++) + c[i]=0; + } + + boolean matchCookie(byte[] input) { + if(input == null || input.length < cookie.length) return false; + for(int i=0; i < cookie.length; i++) + if(cookie[i] != input[i]) return false; + return true; + } + + + String printCookie(byte[] c) { + if(c == null) return ""; + return new String(c); + } + + + public void run() { + byte[] buf=new byte[256]; // start with 256, increase as we go + int len=0; + + while(receiverThread != null && receiverThread.equals(Thread.currentThread()) && is_running) { + try { + if(in == null) { + if(log.isErrorEnabled()) log.error("input stream is null !"); + break; + } + len=in.readInt(); + if(len > buf.length) + buf=new byte[len]; + in.readFully(buf, 0, len); + updateLastAccessed(); + receive(peer_addr, buf, 0, len); // calls receiver.receive(msg) + } + catch(OutOfMemoryError mem_ex) { + if(log.isWarnEnabled()) log.warn("dropped invalid message, closing connection"); + break; // continue; + } + catch(IOException io_ex) { + //this is very common occurrence, hence log under trace level + if(log.isTraceEnabled()) log.trace("Excption while read blocked for data from peer ", io_ex); + notifyConnectionClosed(peer_addr); + break; + } + catch(Throwable e) { + if(log.isWarnEnabled()) log.warn("Problem encountered while receiving message from peer " + peer_addr, e); + } + } + if(log.isTraceEnabled()) + log.trace("ConnectionTable.Connection.Receiver terminated"); + receiverThread=null; + closeSocket(); + // remove(peer_addr); + } + + + public String toString() { + StringBuilder ret=new StringBuilder(); + InetAddress local=null, remote=null; + String local_str, remote_str; + + Socket tmp_sock=sock; + if(tmp_sock == null) + ret.append(""); + else { + //since the sock variable gets set to null we want to make + //make sure we make it through here without a nullpointer exception + local=tmp_sock.getLocalAddress(); + remote=tmp_sock.getInetAddress(); + local_str=local != null ? Util.shortName(local) : ""; + remote_str=remote != null ? Util.shortName(remote) : ""; + ret.append('<' + local_str + ':' + tmp_sock.getLocalPort() + + " --> " + remote_str + ':' + tmp_sock.getPort() + "> (" + + ((System.currentTimeMillis() - last_access) / 1000) + " secs old)"); + } + tmp_sock=null; + + return ret.toString(); + } + + + void closeSocket() { + Util.close(sock); // should actually close in/out (so we don't need to close them explicitly) + sock=null; + Util.close(out); // flushes data + // removed 4/22/2003 (request by Roland Kurmann) + // out=null; + Util.close(in); + } + + + class Sender implements Runnable { + Thread senderThread; + private boolean is_it_running=false; + + void start() { + if(senderThread == null || !senderThread.isAlive()) { + senderThread=getThreadFactory().newThread(thread_group,this, "ConnectionTable.Connection.Sender local_addr=" + local_addr + " [" + getSockAddress() + "]"); + senderThread.setDaemon(true); + is_it_running=true; + senderThread.start(); + if(log.isTraceEnabled()) + log.trace("sender thread started: " + senderThread); + } + } + + void stop() { + is_it_running=false; + if(send_queue != null) + send_queue.clear(); + if(senderThread != null) { + Thread tmp=senderThread; + senderThread=null; + Util.interruptAndWaitToDie(tmp); + } + } + + boolean isRunning() { + return is_it_running && senderThread != null; + } + + public void run() { + byte[] data; + while(senderThread != null && senderThread.equals(Thread.currentThread()) && is_it_running) { + try { + data=send_queue.take(); + if(data == null) + continue; + // we don't need to serialize access to 'out' as we're the only thread sending messages + _send(data, 0, data.length, false); + } + catch(InterruptedException e) { + ; + } + } + is_it_running=false; + if(log.isTraceEnabled()) + log.trace("ConnectionTable.Connection.Sender thread terminated"); + } + } + + + } + + class Reaper implements Runnable { + Thread t=null; + + Reaper() { + ; + } + + // return true if we have zero connections + private boolean haveZeroConnections() { + synchronized(conns) { + return conns.isEmpty(); + } + } + + public void start() { + + if(haveZeroConnections()) + return; + if(t != null && !t.isAlive()) + t=null; + if(t == null) { + //RKU 7.4.2003, put in threadgroup + t=getThreadFactory().newThread(thread_group, this, "ConnectionTable.ReaperThread"); + t.setDaemon(true); // will allow us to terminate if all remaining threads are daemons + t.start(); + } + } + + public void stop() { + Thread tmp=t; + if(t != null) + t=null; + if(tmp != null) { + Util.interruptAndWaitToDie(tmp); + } + } + + + public boolean isRunning() { + return t != null; + } + + public void run() { + Connection connection; + Entry entry; + long curr_time; + + if(log.isDebugEnabled()) log.debug("connection reaper thread was started. Number of connections=" + + conns.size() + ", reaper_interval=" + reaper_interval + ", conn_expire_time=" + + conn_expire_time); + + while(!haveZeroConnections() && t != null && t.equals(Thread.currentThread())) { + Util.sleep(reaper_interval); + if(t == null || !Thread.currentThread().equals(t)) + break; + synchronized(conns) { + curr_time=System.currentTimeMillis(); + for(Iterator> it=conns.entrySet().iterator(); it.hasNext();) { + entry=it.next(); + connection=entry.getValue(); + if(log.isTraceEnabled()) log.trace("connection is " + + ((curr_time - connection.last_access) / 1000) + " seconds old (curr-time=" + + curr_time + ", last_access=" + connection.last_access + ')'); + if(connection.last_access + conn_expire_time < curr_time) { + if(log.isTraceEnabled()) log.trace("connection " + connection + + " has been idle for too long (conn_expire_time=" + conn_expire_time + + "), will be removed"); + connection.destroy(); + it.remove(); + } + } + } + } + if(log.isDebugEnabled()) log.debug("reaper terminated"); + t=null; + } + } +} Index: 3rdParty_sources/jgroups/org/jgroups/blocks/ConnectionTable.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/blocks/ConnectionTable.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/blocks/ConnectionTable.java 17 Aug 2012 14:51:18 -0000 1.1 @@ -0,0 +1,375 @@ +// $Id: ConnectionTable.java,v 1.1 2012/08/17 14:51:18 marcin Exp $ + +package org.jgroups.blocks; + +import org.jgroups.Address; +import org.jgroups.util.PortsManager; +import org.jgroups.stack.IpAddress; + +import java.io.IOException; +import java.net.*; + + +/** + * Manages incoming and outgoing TCP connections. For each outgoing message to destination P, if there + * is not yet a connection for P, one will be created. Subsequent outgoing messages will use this + * connection. For incoming messages, one server socket is created at startup. For each new incoming + * client connecting, a new thread from a thread pool is allocated and listens for incoming messages + * until the socket is closed by the peer.
Sockets/threads with no activity will be killed + * after some time. + *

+ * Incoming messages from any of the sockets can be received by setting the message listener. + * @author Bela Ban + */ +public class ConnectionTable extends BasicConnectionTable implements Runnable { + + /** + * Regular ConnectionTable without expiration of idle connections + * @param srv_port The port on which the server will listen. If this port is reserved, the next + * free port will be taken (incrementing srv_port). + */ + public ConnectionTable(int srv_port) throws Exception { + this.srv_port=srv_port; + init(); + } + + + public ConnectionTable(InetAddress bind_addr, int srv_port) throws Exception { + this.srv_port=srv_port; + this.bind_addr=bind_addr; + init(); + } + + + /** + * ConnectionTable including a connection reaper. Connections that have been idle for more than conn_expire_time + * milliseconds will be closed and removed from the connection table. On next access they will be re-created. + * @param srv_port The port on which the server will listen + * @param reaper_interval Number of milliseconds to wait for reaper between attepts to reap idle connections + * @param conn_expire_time Number of milliseconds a connection can be idle (no traffic sent or received until + * it will be reaped + */ + public ConnectionTable(int srv_port, long reaper_interval, long conn_expire_time) throws Exception { + this.srv_port=srv_port; + this.reaper_interval=reaper_interval; + this.conn_expire_time=conn_expire_time; + use_reaper=true; + init(); + } + + + public ConnectionTable(Receiver r, InetAddress bind_addr, InetAddress external_addr, + int srv_port, int max_port) throws Exception { + setReceiver(r); + this.bind_addr=bind_addr; + this.external_addr=external_addr; + this.srv_port=srv_port; + this.max_port=max_port; + init(); + } + + + /** + * Create a ConnectionTable + * @param r A reference to a receiver of all messages received by this class. Method receive() + * will be called. + * @param bind_addr The host name or IP address of the interface to which the server socket will bind. + * This is interesting only in multi-homed systems. If bind_addr is null, the + * server socket will bind to the first available interface (e.g. /dev/hme0 on + * Solaris or /dev/eth0 on Linux systems). + * @param external_addr The address which will be broadcast to the group (the externally visible address + * which this host should be contacted on). If external_addr is null, it will default to + * the same address that the server socket is bound to. + * @param srv_port The port to which the server socket will bind to. If this port is reserved, the next + * free port will be taken (incrementing srv_port). + * @param max_port The largest port number that the server socket will be bound to. If max_port < srv_port + * then there is no limit. + */ + public ConnectionTable(Receiver r, InetAddress bind_addr, InetAddress external_addr, + int srv_port, int max_port, PortsManager pm) throws Exception { + setReceiver(r); + this.bind_addr=bind_addr; + this.external_addr=external_addr; + this.srv_port=srv_port; + this.max_port=max_port; + this.pm=pm; + init(); + } + + + public ConnectionTable(Receiver r, InetAddress bind_addr, InetAddress external_addr, int srv_port, int max_port, + long reaper_interval, long conn_expire_time) throws Exception { + setReceiver(r); + this.bind_addr=bind_addr; + this.external_addr=external_addr; + this.srv_port=srv_port; + this.max_port=max_port; + this.reaper_interval=reaper_interval; + this.conn_expire_time=conn_expire_time; + use_reaper=true; + init(); + } + + /** + * ConnectionTable including a connection reaper. Connections that have been idle for more than conn_expire_time + * milliseconds will be closed and removed from the connection table. On next access they will be re-created. + * + * @param r The Receiver + * @param bind_addr The host name or IP address of the interface to which the server socket will bind. + * This is interesting only in multi-homed systems. If bind_addr is null, the + * server socket will bind to the first available interface (e.g. /dev/hme0 on + * Solaris or /dev/eth0 on Linux systems). + * @param external_addr The address which will be broadcast to the group (the externally visible address + * which this host should be contacted on). If external_addr is null, it will default to + * the same address that the server socket is bound to. + * @param srv_port The port to which the server socket will bind to. If this port is reserved, the next + * free port will be taken (incrementing srv_port). + * @param max_port The largest port number that the server socket will be bound to. If max_port < srv_port + * then there is no limit. + * @param reaper_interval Number of milliseconds to wait for reaper between attepts to reap idle connections + * @param conn_expire_time Number of milliseconds a connection can be idle (no traffic sent or received until + * it will be reaped + */ + public ConnectionTable(Receiver r, InetAddress bind_addr, InetAddress external_addr, int srv_port, int max_port, + long reaper_interval, long conn_expire_time, PortsManager pm) throws Exception { + setReceiver(r); + this.bind_addr=bind_addr; + this.external_addr=external_addr; + this.srv_port=srv_port; + this.max_port=max_port; + this.pm=pm; + this.reaper_interval=reaper_interval; + this.conn_expire_time=conn_expire_time; + use_reaper=true; + init(); + } + + + + /** Try to obtain correct Connection (or create one if not yet existent) */ + Connection getConnection(Address dest) throws Exception { + Connection conn=null; + Socket sock; + + synchronized(conns) { + conn=conns.get(dest); + if(conn == null) { + // changed by bela Jan 18 2004: use the bind address for the client sockets as well + SocketAddress tmpBindAddr=new InetSocketAddress(bind_addr, 0); + InetAddress tmpDest=((IpAddress)dest).getIpAddress(); + SocketAddress destAddr=new InetSocketAddress(tmpDest, ((IpAddress)dest).getPort()); + sock=new Socket(); + sock.bind(tmpBindAddr); + sock.setKeepAlive(true); + sock.setTcpNoDelay(tcp_nodelay); + if(linger > 0) + sock.setSoLinger(true, linger); + else + sock.setSoLinger(false, -1); + sock.connect(destAddr, sock_conn_timeout); + + try { + sock.setSendBufferSize(send_buf_size); + } + catch(IllegalArgumentException ex) { + if(log.isErrorEnabled()) log.error("exception setting send buffer size to " + + send_buf_size + " bytes", ex); + } + try { + sock.setReceiveBufferSize(recv_buf_size); + } + catch(IllegalArgumentException ex) { + if(log.isErrorEnabled()) log.error("exception setting receive buffer size to " + + send_buf_size + " bytes", ex); + } + conn=new Connection(sock, dest); + conn.sendLocalAddress(local_addr); + notifyConnectionOpened(dest); + addConnection(dest, conn); + conn.init(); + if(log.isTraceEnabled()) log.trace("created socket to " + dest); + } + return conn; + } + } + + + public final void start() throws Exception { + acceptor=getThreadFactory().newThread(thread_group,this, "ConnectionTable.AcceptorThread"); + acceptor.start(); + + // start the connection reaper - will periodically remove unused connections + if(use_reaper && reaper == null) { + reaper=new Reaper(); + reaper.start(); + } + super.start(); + } + + protected void init() throws Exception { + + srv_sock=createServerSocket(srv_port, max_port); + + if (external_addr!=null) + local_addr=new IpAddress(external_addr, srv_sock.getLocalPort()); + else if (bind_addr != null) + local_addr=new IpAddress(bind_addr, srv_sock.getLocalPort()); + else + local_addr=new IpAddress(srv_sock.getLocalPort()); + + if(log.isDebugEnabled()) log.debug("server socket listening on " + local_addr); + } + + + + + /** + * Acceptor thread. Continuously accept new connections. Create a new thread for each new + * connection and put it in conns. When the thread should stop, it is + * interrupted by the thread creator. + */ + public void run() { + Socket client_sock=null; + Connection conn=null; + Address peer_addr; + + while(srv_sock != null) { + try { + conn=null; + client_sock=srv_sock.accept(); + if(!running) { + if(log.isWarnEnabled()) + log.warn("cannot accept connection from " + client_sock.getRemoteSocketAddress() + " as I'm closed"); + break; + } + if(log.isTraceEnabled()) + log.trace("[" +local_addr + "] accepted connection from " + client_sock.getInetAddress() + ":" + client_sock.getPort()); + try { + client_sock.setSendBufferSize(send_buf_size); + } + catch(IllegalArgumentException ex) { + if(log.isErrorEnabled()) log.error("exception setting send buffer size to " + send_buf_size + " bytes", ex); + } + try { + client_sock.setReceiveBufferSize(recv_buf_size); + } + catch(IllegalArgumentException ex) { + if(log.isErrorEnabled()) log.error("exception setting receive buffer size to " + send_buf_size + " bytes", ex); + } + + client_sock.setKeepAlive(true); + client_sock.setTcpNoDelay(tcp_nodelay); + if(linger > 0) + client_sock.setSoLinger(true, linger); + else + client_sock.setSoLinger(false, -1); + + // create new thread and add to conn table + conn=new Connection(client_sock, null); // will call receive(msg) + // get peer's address + peer_addr=conn.readPeerAddress(client_sock); + + // client_addr=new IpAddress(client_sock.getInetAddress(), client_port); + conn.setPeerAddress(peer_addr); + + synchronized(conns) { + Connection tmp=conns.get(peer_addr); + //Vladimir Nov, 5th, 2007 + //we might have a connection to peer but is that + //connection still open? + boolean connectionOpen = tmp != null && !tmp.isSocketClosed(); + if(connectionOpen) { + if(peer_addr.compareTo(local_addr) > 0) { + if(log.isTraceEnabled()) + log.trace("peer's address (" + peer_addr + ") is greater than our local address (" + + local_addr + "), replacing our existing connection"); + // peer's address is greater, add peer's connection to ConnectionTable, destroy existing connection + removeConnection(peer_addr); + addConnection(peer_addr, conn); + notifyConnectionOpened(peer_addr); + } + else { + if(log.isTraceEnabled()) + log.trace("peer's address (" + peer_addr + ") is smaller than our local address (" + + local_addr + "), rejecting peer connection request"); + conn.destroy(); + continue; + } + } + else { + addConnection(peer_addr, conn); + notifyConnectionOpened(peer_addr); + } + } + + conn.init(); // starts handler thread on this socket + } + catch(SocketTimeoutException timeout_ex) { + if(log.isWarnEnabled()) log.warn("timed out waiting for peer address, closing connection " + conn + ": " + timeout_ex); + if(conn != null) + conn.destroy(); + if(srv_sock == null) + break; // socket was closed, therefore stop + } + catch(SocketException sock_ex) { + if(log.isWarnEnabled() && srv_sock != null) log.warn("Could not read accept connection from peer " + sock_ex); + if(conn != null) + conn.destroy(); + if(srv_sock == null) + break; // socket was closed, therefore stop + } + catch(Throwable ex) { + if(log.isWarnEnabled()) log.warn("Could not read accept connection from peer " + ex); + if(srv_sock == null) + break; // socket was closed, therefore stop + } + } + if(client_sock != null) + try {client_sock.close();} catch(IOException e) {} + if(log.isTraceEnabled()) + log.trace(Thread.currentThread().getName() + " terminated"); + } + + + /** Finds first available port starting at start_port and returns server socket. + * Will not bind to port >end_port. Sets srv_port */ + protected ServerSocket createServerSocket(int start_port, int end_port) throws Exception { + ServerSocket ret=null; + + while(true) { + try { + if(start_port > 0 && pm != null) + start_port=pm.getNextAvailablePort(start_port); + if(bind_addr == null) + ret=new ServerSocket(start_port); + else { + // changed (bela Sept 7 2007): we accept connections on all NICs + ret=new ServerSocket(start_port, backlog, bind_addr); + } + } + catch(BindException bind_ex) { + if (start_port==end_port) throw new BindException("No available port to bind to"); + if(bind_addr != null && !bind_addr.isLoopbackAddress()) { + NetworkInterface nic=NetworkInterface.getByInetAddress(bind_addr); + if(nic == null) + throw new BindException("bind_addr " + bind_addr + " is not a valid interface"); + } + start_port++; + continue; + } + catch(IOException io_ex) { + if(log.isErrorEnabled()) log.error("exception is " + io_ex); + } + srv_port=start_port; + break; + } + if(ret == null) + throw new IOException("Could not create server socket in port range " + start_port + " - " +end_port); + return ret; + } + + + + +} + Index: 3rdParty_sources/jgroups/org/jgroups/blocks/ConnectionTableNIO.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/blocks/ConnectionTableNIO.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/blocks/ConnectionTableNIO.java 17 Aug 2012 14:51:18 -0000 1.1 @@ -0,0 +1,1540 @@ +// $Id: ConnectionTableNIO.java,v 1.1 2012/08/17 14:51:18 marcin Exp $ + +package org.jgroups.blocks; + +import org.apache.commons.logging.Log; +import org.jgroups.Address; +import org.jgroups.Global; +import org.jgroups.stack.IpAddress; +import org.jgroups.util.PortsManager; +import org.jgroups.util.ShutdownRejectedExecutionHandler; + +import java.io.IOException; +import java.net.*; +import java.nio.ByteBuffer; +import java.nio.channels.*; +import java.nio.channels.spi.SelectorProvider; +import java.util.*; +import java.util.concurrent.*; + +/** + * Manages incoming and outgoing TCP connections. For each outgoing message to destination P, if there + * is not yet a connection for P, one will be created. Subsequent outgoing messages will use this + * connection. For incoming messages, one server socket is created at startup. For each new incoming + * client connecting, a new thread from a thread pool is allocated and listens for incoming messages + * until the socket is closed by the peer.
Sockets/threads with no activity will be killed + * after some time. + *

+ * Incoming messages from any of the sockets can be received by setting the message listener. + * + * We currently require use_incoming_packet_handler=true (release 2.4 will support use_incoming_packet_handler=false + * due to threadless stack support). + * + * @author Bela Ban, Scott Marlow, Alex Fu + */ +public class ConnectionTableNIO extends BasicConnectionTable implements Runnable { + + private ServerSocketChannel m_serverSocketChannel; + private Selector m_acceptSelector; + + private WriteHandler[] m_writeHandlers; + private int m_nextWriteHandler = 0; + private final Object m_lockNextWriteHandler = new Object(); + + private ReadHandler[] m_readHandlers; + private int m_nextReadHandler = 0; + private final Object m_lockNextReadHandler = new Object(); + + // thread pool for processing read requests + private Executor m_requestProcessors; + private volatile boolean serverStopping=false; + + private final List m_backGroundThreads = new LinkedList(); // Collection of all created threads + + private int m_reader_threads = 3; + + private int m_writer_threads = 3; + + private int m_processor_threads = 5; // PooledExecutor.createThreads() + private int m_processor_minThreads = 5; // PooledExecutor.setMinimumPoolSize() + private int m_processor_maxThreads = 5; // PooledExecutor.setMaxThreads() + private int m_processor_queueSize=100; // Number of queued requests that can be pending waiting + // for a background thread to run the request. + private long m_processor_keepAliveTime = Long.MAX_VALUE; // PooledExecutor.setKeepAliveTime( milliseconds); + // negative value used to mean to wait forever, instead set to Long.MAX_VALUE to wait forever + + + + /** + * @param srv_port + * @throws Exception + */ + public ConnectionTableNIO(int srv_port) throws Exception { + this.srv_port=srv_port; + start(); + } + + /** + * @param srv_port + * @param reaper_interval + * @param conn_expire_time + * @throws Exception + */ + public ConnectionTableNIO(int srv_port, long reaper_interval, + long conn_expire_time) throws Exception { + this.srv_port=srv_port; + this.reaper_interval=reaper_interval; + this.conn_expire_time=conn_expire_time; + start(); + } + + /** + * @param r + * @param bind_addr + * @param external_addr + * @param srv_port + * @param max_port + * @throws Exception + */ + public ConnectionTableNIO(Receiver r, InetAddress bind_addr, InetAddress external_addr, int srv_port, int max_port) + throws Exception + { + setReceiver(r); + this.external_addr=external_addr; + this.bind_addr=bind_addr; + this.srv_port=srv_port; + this.max_port=max_port; + use_reaper=true; + start(); + } + + + public ConnectionTableNIO(Receiver r, InetAddress bind_addr, InetAddress external_addr, + int srv_port, int max_port, boolean doStart) + throws Exception + { + setReceiver(r); + this.external_addr=external_addr; + this.bind_addr=bind_addr; + this.srv_port=srv_port; + this.max_port=max_port; + use_reaper=true; + if(doStart) + start(); + } + + public ConnectionTableNIO(Receiver r, InetAddress bind_addr, InetAddress external_addr, + int srv_port, int max_port, PortsManager pm, + boolean doStart) + throws Exception + { + setReceiver(r); + this.external_addr=external_addr; + this.bind_addr=bind_addr; + this.srv_port=srv_port; + this.max_port=max_port; + this.pm=pm; + use_reaper=true; + if(doStart) + start(); + } + + + /** + * @param r + * @param bind_addr + * @param external_addr + * @param srv_port + * @param max_port + * @param reaper_interval + * @param conn_expire_time + * @throws Exception + */ + public ConnectionTableNIO(Receiver r, InetAddress bind_addr, InetAddress external_addr, int srv_port, int max_port, + long reaper_interval, long conn_expire_time + ) throws Exception + { + setReceiver(r); + this.bind_addr=bind_addr; + this.external_addr=external_addr; + this.srv_port=srv_port; + this.max_port=max_port; + this.reaper_interval=reaper_interval; + this.conn_expire_time=conn_expire_time; + use_reaper=true; + start(); + } + + + public ConnectionTableNIO(Receiver r, InetAddress bind_addr, InetAddress external_addr, + int srv_port, int max_port, + long reaper_interval, long conn_expire_time, boolean doStart + ) throws Exception + { + setReceiver(r); + this.bind_addr=bind_addr; + this.external_addr=external_addr; + this.srv_port=srv_port; + this.max_port=max_port; + this.reaper_interval=reaper_interval; + this.conn_expire_time=conn_expire_time; + use_reaper=true; + if(doStart) + start(); + } + + public ConnectionTableNIO(Receiver r, InetAddress bind_addr, InetAddress external_addr, + int srv_port, int max_port, PortsManager pm, + long reaper_interval, long conn_expire_time, boolean doStart + ) throws Exception + { + setReceiver(r); + this.bind_addr=bind_addr; + this.external_addr=external_addr; + this.srv_port=srv_port; + this.max_port=max_port; + this.pm=pm; + this.reaper_interval=reaper_interval; + this.conn_expire_time=conn_expire_time; + use_reaper=true; + if(doStart) + start(); + } + + + + public int getReaderThreads() { return m_reader_threads; } + + public void setReaderThreads(int m_reader_threads) { + this.m_reader_threads=m_reader_threads; + } + + public int getWriterThreads() { return m_writer_threads; } + + public void setWriterThreads(int m_writer_threads) { + this.m_writer_threads=m_writer_threads; + } + + public int getProcessorThreads() { return m_processor_threads; } + + public void setProcessorThreads(int m_processor_threads) { + this.m_processor_threads=m_processor_threads; + } + + public int getProcessorMinThreads() { return m_processor_minThreads;} + + public void setProcessorMinThreads(int m_processor_minThreads) { + this.m_processor_minThreads=m_processor_minThreads; + } + + public int getProcessorMaxThreads() { return m_processor_maxThreads;} + + public void setProcessorMaxThreads(int m_processor_maxThreads) { + this.m_processor_maxThreads=m_processor_maxThreads; + } + + public int getProcessorQueueSize() { return m_processor_queueSize; } + + public void setProcessorQueueSize(int m_processor_queueSize) { + this.m_processor_queueSize=m_processor_queueSize; + } + + public long getProcessorKeepAliveTime() { return m_processor_keepAliveTime; } + + public void setProcessorKeepAliveTime(long m_processor_keepAliveTime) { + this.m_processor_keepAliveTime=m_processor_keepAliveTime; + } + + + /** + * Try to obtain correct Connection (or create one if not yet existent) + */ + ConnectionTable.Connection getConnection(Address dest) throws Exception + { + Connection conn; + SocketChannel sock_ch; + + synchronized (conns) + { + conn = (Connection) conns.get(dest); + if (conn == null) + { + InetSocketAddress destAddress = new InetSocketAddress(((IpAddress) dest).getIpAddress(), + ((IpAddress) dest).getPort()); + sock_ch = SocketChannel.open(destAddress); + sock_ch.socket().setTcpNoDelay(tcp_nodelay); + conn = new Connection(sock_ch, dest); + + conn.sendLocalAddress(local_addr); + // This outbound connection is ready + + sock_ch.configureBlocking(false); + + try + { + if (log.isTraceEnabled()) + log.trace("About to change new connection send buff size from " + sock_ch.socket().getSendBufferSize() + " bytes"); + sock_ch.socket().setSendBufferSize(send_buf_size); + if (log.isTraceEnabled()) + log.trace("Changed new connection send buff size to " + sock_ch.socket().getSendBufferSize() + " bytes"); + } + catch (IllegalArgumentException ex) + { + if (log.isErrorEnabled()) log.error("exception setting send buffer size to " + + send_buf_size + " bytes: " + ex); + } + try + { + if (log.isTraceEnabled()) + log.trace("About to change new connection receive buff size from " + sock_ch.socket().getReceiveBufferSize() + " bytes"); + sock_ch.socket().setReceiveBufferSize(recv_buf_size); + if (log.isTraceEnabled()) + log.trace("Changed new connection receive buff size to " + sock_ch.socket().getReceiveBufferSize() + " bytes"); + } + catch (IllegalArgumentException ex) + { + if (log.isErrorEnabled()) log.error("exception setting receive buffer size to " + + send_buf_size + " bytes: " + ex); + } + + int idx; + synchronized (m_lockNextWriteHandler) + { + idx = m_nextWriteHandler = (m_nextWriteHandler + 1) % m_writeHandlers.length; + } + conn.setupWriteHandler(m_writeHandlers[idx]); + + // Put the new connection to the queue + try + { + synchronized (m_lockNextReadHandler) + { + idx = m_nextReadHandler = (m_nextReadHandler + 1) % m_readHandlers.length; + } + m_readHandlers[idx].add(conn); + + } catch (InterruptedException e) + { + if (log.isWarnEnabled()) + log.warn("Thread (" +Thread.currentThread().getName() + ") was interrupted, closing connection", e); + // What can we do? Remove it from table then. + conn.destroy(); + throw e; + } + + // Add connection to table + addConnection(dest, conn); + + notifyConnectionOpened(dest); + if (log.isTraceEnabled()) log.trace("created socket to " + dest); + } + return conn; + } + } + + public final void start() throws Exception { + super.start(); + init(); + srv_sock=createServerSocket(srv_port, max_port); + + if (external_addr!=null) + local_addr=new IpAddress(external_addr, srv_sock.getLocalPort()); + else if (bind_addr != null) + local_addr=new IpAddress(bind_addr, srv_sock.getLocalPort()); + else + local_addr=new IpAddress(srv_sock.getLocalPort()); + + if(log.isDebugEnabled()) log.debug("server socket created on " + local_addr); + + + //Roland Kurmann 4/7/2003, put in thread_group + acceptor=getThreadFactory().newThread(thread_group, this, "ConnectionTable.AcceptorThread"); + acceptor.setDaemon(true); + acceptor.start(); + m_backGroundThreads.add(acceptor); + + // start the connection reaper - will periodically remove unused connections + if(use_reaper && reaper == null) { + reaper=new Reaper(); + reaper.start(); + } + } + + protected void init() + throws Exception + { + + // use directExector if max thread pool size is less than or equal to zero. + if(getProcessorMaxThreads() <= 0) { + m_requestProcessors = new Executor() { + + public void execute(Runnable command) { + command.run(); + } + }; + } + else + { + // Create worker thread pool for processing incoming buffers + ThreadPoolExecutor requestProcessors = new ThreadPoolExecutor(getProcessorMinThreads(), getProcessorMaxThreads(), + getProcessorKeepAliveTime(), TimeUnit.MILLISECONDS, + new LinkedBlockingQueue(getProcessorQueueSize())); + + requestProcessors.setThreadFactory(new ThreadFactory() { + public Thread newThread(Runnable runnable) { + Thread new_thread=new Thread(thread_group, runnable); + new_thread.setDaemon(true); + new_thread.setName("ConnectionTableNIO.Thread"); + m_backGroundThreads.add(new_thread); + return new_thread; + } + }); + requestProcessors.setRejectedExecutionHandler(new ShutdownRejectedExecutionHandler(requestProcessors.getRejectedExecutionHandler())); + m_requestProcessors = requestProcessors; + } + + m_writeHandlers = WriteHandler.create(getThreadFactory(),getWriterThreads(), thread_group, m_backGroundThreads, log); + m_readHandlers = ReadHandler.create(getThreadFactory(),getReaderThreads(), this, thread_group, m_backGroundThreads, log); + } + + + /** + * Closes all open sockets, the server socket and all threads waiting for incoming messages + */ + public void stop() + { + super.stop(); + serverStopping = true; + + if(reaper != null) + reaper.stop(); + + // Stop the main selector + if(m_acceptSelector != null) + m_acceptSelector.wakeup(); + + // Stop selector threads + if(m_readHandlers != null) + { + for (int i = 0; i < m_readHandlers.length; i++) + { + try + { + m_readHandlers[i].add(new Shutdown()); + } catch (InterruptedException e) + { + log.error("Thread ("+Thread.currentThread().getName() +") was interrupted, failed to shutdown selector", e); + } + } + } + if(m_writeHandlers != null) + { + for (int i = 0; i < m_writeHandlers.length; i++) + { + try + { + m_writeHandlers[i].queue.put(new Shutdown()); + m_writeHandlers[i].selector.wakeup(); + } catch (InterruptedException e) + { + log.error("Thread ("+Thread.currentThread().getName() +") was interrupted, failed to shutdown selector", e); + } + } + } + + // Stop the callback thread pool + if(m_requestProcessors instanceof ThreadPoolExecutor) + ((ThreadPoolExecutor)m_requestProcessors).shutdownNow(); + + if(m_requestProcessors instanceof ThreadPoolExecutor){ + try{ + ((ThreadPoolExecutor) m_requestProcessors).awaitTermination(Global.THREADPOOL_SHUTDOWN_WAIT_TIME, + TimeUnit.MILLISECONDS); + }catch(InterruptedException e){ + } + } + + // then close the connections + synchronized(conns) { + Iterator it=conns.values().iterator(); + while(it.hasNext()) { + Connection conn=(Connection)it.next(); + conn.destroy(); + } + conns.clear(); + } + + while(!m_backGroundThreads.isEmpty()) { + Thread t =m_backGroundThreads.remove(0); + try { + t.join(); + } catch(InterruptedException e) { + log.error("Thread ("+Thread.currentThread().getName() +") was interrupted while waiting on thread " + t.getName() + " to finish."); + } + } + m_backGroundThreads.clear(); + + } + + /** + * Acceptor thread. Continuously accept new connections and assign readhandler/writehandler + * to them. + */ + public void run() { + Connection conn; + + while(m_serverSocketChannel.isOpen() && !serverStopping) { + int num; + try { + num=m_acceptSelector.select(); + } + catch(IOException e) { + if(log.isWarnEnabled()) + log.warn("Select operation on listening socket failed", e); + continue; // Give up this time + } + + if(num > 0) { + Set readyKeys=m_acceptSelector.selectedKeys(); + for(Iterator i=readyKeys.iterator(); i.hasNext();) { + SelectionKey key=i.next(); + i.remove(); + // We only deal with new incoming connections + + ServerSocketChannel readyChannel=(ServerSocketChannel)key.channel(); + SocketChannel client_sock_ch; + try { + client_sock_ch=readyChannel.accept(); + } + catch(IOException e) { + if(log.isWarnEnabled()) + log.warn("Attempt to accept new connection from listening socket failed", e); + // Give up this connection + continue; + } + + if(log.isTraceEnabled()) + log.trace("accepted connection, client_sock=" + client_sock_ch.socket()); + + try { + client_sock_ch.socket().setSendBufferSize(send_buf_size); + } + catch(IllegalArgumentException ex) { + if(log.isErrorEnabled()) log.error("exception setting send buffer size to " + send_buf_size + " bytes: ", ex); + } + catch(SocketException e) { + if(log.isErrorEnabled()) log.error("exception setting send buffer size to " + send_buf_size + " bytes: ", e); + } + + try { + client_sock_ch.socket().setReceiveBufferSize(recv_buf_size); + } + catch(IllegalArgumentException ex) { + if(log.isErrorEnabled()) log.error("exception setting receive buffer size to " + send_buf_size + " bytes: ", ex); + } + catch(SocketException e) { + if(log.isErrorEnabled()) log.error("exception setting receive buffer size to " + recv_buf_size + " bytes: ", e); + } + + conn=new Connection(client_sock_ch, null); + try { + Address peer_addr=conn.readPeerAddress(client_sock_ch.socket()); + conn.peer_addr=peer_addr; + synchronized(conns) { + Connection tmp=(Connection)conns.get(peer_addr); + if(tmp != null) { + if(peer_addr.compareTo(local_addr) > 0) { + if(log.isTraceEnabled()) + log.trace("peer's address (" + peer_addr + ") is greater than our local address (" + + local_addr + "), replacing our existing connection"); + // peer's address is greater, add peer's connection to ConnectionTable, destroy existing connection + addConnection(peer_addr, conn); + tmp.destroy(); + notifyConnectionOpened(peer_addr); + } + else { + if(log.isTraceEnabled()) + log.trace("peer's address (" + peer_addr + ") is smaller than our local address (" + + local_addr + "), rejecting peer connection request"); + conn.destroy(); + continue; + } + } + else { + addConnection(peer_addr, conn); + } + } + notifyConnectionOpened(peer_addr); + client_sock_ch.configureBlocking(false); + } + catch(IOException e) { + if(log.isWarnEnabled()) + log.warn("Attempt to configure non-blocking mode failed", e); + conn.destroy(); + continue; + } + catch(Exception e) { + if(log.isWarnEnabled()) + log.warn("Attempt to handshake with other peer failed", e); + conn.destroy(); + continue; + } + + int idx; + synchronized(m_lockNextWriteHandler) { + idx=m_nextWriteHandler=(m_nextWriteHandler + 1) % m_writeHandlers.length; + } + conn.setupWriteHandler(m_writeHandlers[idx]); + + try { + synchronized(m_lockNextReadHandler) { + idx=m_nextReadHandler=(m_nextReadHandler + 1) % m_readHandlers.length; + } + m_readHandlers[idx].add(conn); + + } + catch(InterruptedException e) { + if(log.isWarnEnabled()) + log.warn("Attempt to configure read handler for accepted connection failed", e); + // close connection + conn.destroy(); + } + } // end of iteration + } // end of selected key > 0 + } // end of thread + + if(m_serverSocketChannel.isOpen()) { + try { + m_serverSocketChannel.close(); + } + catch(Exception e) { + log.error("exception closing server listening socket", e); + } + } + if(log.isTraceEnabled()) + log.trace("acceptor thread terminated"); + + } + + + /** + * Finds first available port starting at start_port and returns server socket. Sets srv_port + */ + protected ServerSocket createServerSocket(int start_port, int end_port) throws Exception + { + this.m_acceptSelector = Selector.open(); + m_serverSocketChannel = ServerSocketChannel.open(); + m_serverSocketChannel.configureBlocking(false); + + if(start_port > 0 && pm != null) + start_port=pm.getNextAvailablePort(start_port); + + while (true) + { + try + { + SocketAddress sockAddr; + if (bind_addr == null) + { + sockAddr=new InetSocketAddress(start_port); + m_serverSocketChannel.socket().bind(sockAddr); + } + else + { + sockAddr=new InetSocketAddress(bind_addr, start_port); + m_serverSocketChannel.socket().bind(sockAddr, backlog); + } + } + catch (BindException bind_ex) + { + if (start_port == end_port) + throw (BindException) ((new BindException("No available port to bind to (start_port=" + start_port + ")")).initCause(bind_ex)); + start_port++; + continue; + } + catch (SocketException bind_ex) + { + if (start_port == end_port) + throw (BindException) ((new BindException("No available port to bind to (start_port=" + start_port + ")")).initCause(bind_ex)); + start_port++; + continue; + } + catch (IOException io_ex) + { + if (log.isErrorEnabled()) log.error("Attempt to bind serversocket failed, port="+start_port+", bind addr=" + bind_addr ,io_ex); + throw io_ex; + } + srv_port = start_port; + break; + } + m_serverSocketChannel.register(this.m_acceptSelector, SelectionKey.OP_ACCEPT); + return m_serverSocketChannel.socket(); + } + + protected void runRequest(Address addr, ByteBuffer buf) throws InterruptedException { + m_requestProcessors.execute(new ExecuteTask(addr, buf)); + } + + + // Represents shutdown + private static class Shutdown { + } + + // ReadHandler has selector to deal with read, it runs in seperated thread + private static class ReadHandler implements Runnable { + private final Selector selector= initHandler(); + private final LinkedBlockingQueue queue= new LinkedBlockingQueue(); + private final ConnectionTableNIO connectTable; + private final Log log; + + ReadHandler(ConnectionTableNIO ct, Log log) { + connectTable= ct; + this.log=log; + } + + public Selector initHandler() + { + // Open the selector + try + { + return Selector.open(); + } catch (IOException e) + { + if (log.isErrorEnabled()) log.error(e); + throw new IllegalStateException(e.getMessage()); + } + + } + + /** + * create instances of ReadHandler threads for receiving data. + * + * @param workerThreads is the number of threads to create. + */ + private static ReadHandler[] create(org.jgroups.util.ThreadFactory f,int workerThreads, ConnectionTableNIO ct, ThreadGroup tg, List backGroundThreads, Log log) + { + ReadHandler[] handlers = new ReadHandler[workerThreads]; + for (int looper = 0; looper < workerThreads; looper++) + { + handlers[looper] = new ReadHandler(ct, log); + + + Thread thread = f.newThread(tg, handlers[looper], "nioReadHandlerThread"); + thread.setDaemon(true); + thread.start(); + backGroundThreads.add(thread); + } + return handlers; + } + + + private void add(Object conn) throws InterruptedException + { + queue.put(conn); + wakeup(); + } + + private void wakeup() + { + selector.wakeup(); + } + + public void run() + { + while (true) + { // m_s can be closed by the management thread + int events; + try + { + events = selector.select(); + } catch (IOException e) + { + if (log.isWarnEnabled()) + log.warn("Select operation on socket failed", e); + continue; // Give up this time + } catch (ClosedSelectorException e) + { + if (log.isWarnEnabled()) + log.warn("Select operation on socket failed" , e); + return; // Selector gets closed, thread stops + } + + if (events > 0) + { // there are read-ready channels + Set readyKeys = selector.selectedKeys(); + try + { + for (Iterator i = readyKeys.iterator(); i.hasNext();) + { + SelectionKey key = (SelectionKey) i.next(); + i.remove(); + // Do partial read and handle call back + Connection conn = (Connection) key.attachment(); + if(conn != null && conn.getSocketChannel() != null) + { + try + { + if (conn.getSocketChannel().isOpen()) + readOnce(conn); + else + { // socket connection is already closed, clean up connection state + conn.closed(); + } + } catch (IOException e) + { + if (log.isTraceEnabled()) log.trace("Read operation on socket failed" , e); + // The connection must be bad, cancel the key, close socket, then + // remove it from table! + key.cancel(); + conn.destroy(); + conn.closed(); + } + } + } + } + catch(ConcurrentModificationException e) { + if (log.isTraceEnabled()) log.trace("Selection set changed", e); + // valid events should still be in the selection set the next time + } + } + + // Now we look at the connection queue to get any new connections added + Object o; + try + { + o = queue.poll(0L, TimeUnit.MILLISECONDS); // get a connection + } catch (InterruptedException e) + { + if (log.isTraceEnabled()) log.trace("Thread ("+Thread.currentThread().getName() +") was interrupted while polling queue" ,e); + // We must give up + continue; + } + if (null == o) + continue; + if (o instanceof Shutdown) { // shutdown command? + try { + selector.close(); + } catch(IOException e) { + if (log.isTraceEnabled()) log.trace("Read selector close operation failed" , e); + } + return; // stop reading + } + Connection conn = (Connection) o;// must be a new connection + SocketChannel sc = conn.getSocketChannel(); + try + { + sc.register(selector, SelectionKey.OP_READ, conn); + } catch (ClosedChannelException e) + { + if (log.isTraceEnabled()) log.trace("Socket channel was closed while we were trying to register it to selector" , e); + // Channel becomes bad. The connection must be bad, + // close socket, then remove it from table! + conn.destroy(); + conn.closed(); + } + } // end of the while true loop + } + + private void readOnce(Connection conn) + throws IOException + { + ConnectionReadState readState = conn.getReadState(); + if (!readState.isHeadFinished()) + { // a brand new message coming or header is not completed + // Begin or continue to read header + int size = readHeader(conn); + if (0 == size) + { // header is not completed + return; + } + } + // Begin or continue to read body + if (readBody(conn) > 0) + { // not finish yet + return; + } + Address addr = conn.getPeerAddress(); + ByteBuffer buf = readState.getReadBodyBuffer(); + // Clear status + readState.bodyFinished(); + // Assign worker thread to execute call back + try + { + connectTable.runRequest(addr, buf); + } catch (InterruptedException e) + { + // Cannot do call back, what can we do? + // Give up handling the message then + log.error("Thread ("+Thread.currentThread().getName() +") was interrupted while assigning executor to process read request" , e); + } + } + + /** + * Read message header from channel. It doesn't try to complete. If there is nothing in + * the channel, the method returns immediately. + * + * @param conn The connection + * @return 0 if header hasn't been read completely, otherwise the size of message body + * @throws IOException + */ + private int readHeader(Connection conn) + throws IOException + { + ConnectionReadState readState = conn.getReadState(); + ByteBuffer headBuf = readState.getReadHeadBuffer(); + + SocketChannel sc = conn.getSocketChannel(); + while (headBuf.remaining() > 0) + { + int num = sc.read(headBuf); + if (-1 == num) + {// EOS + throw new IOException("Peer closed socket"); + } + if (0 == num) // no more data + return 0; + } + // OK, now we get the whole header, change the status and return message size + return readState.headFinished(); + } + + /** + * Read message body from channel. It doesn't try to complete. If there is nothing in + * the channel, the method returns immediately. + * + * @param conn The connection + * @return remaining bytes for the message + * @throws IOException + */ + private int readBody(Connection conn) + throws IOException + { + ByteBuffer bodyBuf = conn.getReadState().getReadBodyBuffer(); + + SocketChannel sc = conn.getSocketChannel(); + while (bodyBuf.remaining() > 0) + { + int num = sc.read(bodyBuf); + if (-1 == num) // EOS + throw new IOException("Couldn't read from socket as peer closed the socket"); + if (0 == num) // no more data + return bodyBuf.remaining(); + } + // OK, we finished reading the whole message! Flip it (not necessary though) + bodyBuf.flip(); + return 0; + } + } + + private class ExecuteTask implements Runnable { + Address m_addr = null; + ByteBuffer m_buf = null; + + public ExecuteTask(Address addr, ByteBuffer buf) + { + m_addr = addr; + m_buf = buf; + } + + public void run() + { + receive(m_addr, m_buf.array(), m_buf.arrayOffset(), m_buf.limit()); + } + } + + private class ConnectionReadState { + private final Connection m_conn; + + // Status for receiving message + private boolean m_headFinished = false; + private ByteBuffer m_readBodyBuf = null; + private final ByteBuffer m_readHeadBuf = ByteBuffer.allocate(Connection.HEADER_SIZE); + + public ConnectionReadState(Connection conn) + { + m_conn = conn; + } + + ByteBuffer getReadBodyBuffer() + { + return m_readBodyBuf; + } + + ByteBuffer getReadHeadBuffer() + { + return m_readHeadBuf; + } + + void bodyFinished() + { + m_headFinished = false; + m_readHeadBuf.clear(); + m_readBodyBuf = null; + m_conn.updateLastAccessed(); + } + + /** + * Status change for finishing reading the message header (data already in buffer) + * + * @return message size + */ + int headFinished() + { + m_headFinished = true; + m_readHeadBuf.flip(); + int messageSize = m_readHeadBuf.getInt(); + m_readBodyBuf = ByteBuffer.allocate(messageSize); + m_conn.updateLastAccessed(); + return messageSize; + } + + boolean isHeadFinished() + { + return m_headFinished; + } + } + + class Connection extends ConnectionTable.Connection { + private SocketChannel sock_ch = null; + private WriteHandler m_writeHandler; + private SelectorWriteHandler m_selectorWriteHandler; + private final ConnectionReadState m_readState; + + private static final int HEADER_SIZE = 4; + final ByteBuffer headerBuffer = ByteBuffer.allocate(HEADER_SIZE); + + Connection(SocketChannel s, Address peer_addr) + { + super(s.socket(), peer_addr); + sock_ch = s; + m_readState = new ConnectionReadState(this); + is_running=true; + } + + private ConnectionReadState getReadState() + { + return m_readState; + } + + private void setupWriteHandler(WriteHandler hdlr) + { + m_writeHandler = hdlr; + m_selectorWriteHandler = hdlr.add(sock_ch); + } + + + + void doSend(byte[] buffie, int offset, int length) throws Exception + { + MyFuture result = new MyFuture(); + m_writeHandler.write(sock_ch, ByteBuffer.wrap(buffie, offset, length), result, m_selectorWriteHandler); + Object ex = result.get(); + if (ex instanceof Exception) + { + if (log.isErrorEnabled()) + log.error("failed sending message", (Exception)ex); + if (((Exception)ex).getCause() instanceof IOException) + throw (IOException) ((Exception)ex).getCause(); + throw (Exception)ex; + } + result.get(); + } + + + SocketChannel getSocketChannel() + { + return sock_ch; + } + + void closeSocket() + { + + if (sock_ch != null) + { + try + { + if(sock_ch.isConnected() && sock_ch.isOpen()) { + sock_ch.close(); + } + } + catch (Exception e) + { + log.error("error closing socket connection", e); + } + sock_ch = null; + } + } + + + void closed() + { + Address peerAddr = getPeerAddress(); + synchronized (conns) + { + conns.remove(peerAddr); + } + notifyConnectionClosed(peerAddr); + } + } + + + /** + * Handle writing to non-blocking NIO connection. + */ + private static class WriteHandler implements Runnable { + // Create a queue for write requests (unbounded) + private final LinkedBlockingQueue queue= new LinkedBlockingQueue(); + + private final Selector selector= initSelector(); + private int m_pendingChannels; // count of the number of channels that have pending writes + // note that this variable is only accessed by one thread. + + // allocate and reuse the header for all buffer write operations + private ByteBuffer m_headerBuffer = ByteBuffer.allocate(Connection.HEADER_SIZE); + private final Log log; + + + public WriteHandler(Log log) { + this.log=log; + } + + Selector initSelector() { + try + { + return SelectorProvider.provider().openSelector(); + } + catch (IOException e) + { + if (log.isErrorEnabled()) log.error(e); + throw new IllegalStateException(e.getMessage()); + } + } + + /** + * create instances of WriteHandler threads for sending data. + * + * @param workerThreads is the number of threads to create. + */ + private static WriteHandler[] create(org.jgroups.util.ThreadFactory f, int workerThreads, ThreadGroup tg, List backGroundThreads, Log log) + { + WriteHandler[] handlers = new WriteHandler[workerThreads]; + for (int looper = 0; looper < workerThreads; looper++) + { + handlers[looper] = new WriteHandler(log); + + Thread thread = f.newThread(tg, handlers[looper], "nioWriteHandlerThread"); + thread.setDaemon(true); + thread.start(); + backGroundThreads.add(thread); + } + return handlers; + } + + /** + * Add a new channel to be handled. + * + * @param channel + */ + private SelectorWriteHandler add(SocketChannel channel) + { + return new SelectorWriteHandler(channel, selector, m_headerBuffer); + } + + /** + * Writes buffer to the specified socket connection. This is always performed asynchronously. If you want + * to perform a synchrounous write, call notification.`get() which will block until the write operation is complete. + * Best practice is to call notification.getException() which may return any exceptions that occured during the write + * operation. + * + * @param channel is where the buffer is written to. + * @param buffer is what we write. + * @param notification may be specified if you want to know how many bytes were written and know if an exception + * occurred. + */ + private void write(SocketChannel channel, ByteBuffer buffer, MyFuture notification, SelectorWriteHandler hdlr) throws InterruptedException + { + queue.put(new WriteRequest(channel, buffer, notification, hdlr)); + } + + private static void close(SelectorWriteHandler entry) + { + entry.cancel(); + } + + private static void handleChannelError( SelectorWriteHandler entry, Throwable error) + { + // notify callers of the exception and drain all of the send buffers for this channel. + do + { + if (error != null) + entry.notifyError(error); + } + while (entry.next()); + close(entry); + } + + // process the write operation + private void processWrite(Selector selector) + { + Set keys = selector.selectedKeys(); + Object arr[] = keys.toArray(); + for (Object anArr : arr) { + SelectionKey key = (SelectionKey) anArr; + SelectorWriteHandler entry = (SelectorWriteHandler) key.attachment(); + boolean needToDecrementPendingChannels = false; + try { + if (0 == entry.write()) { // write the buffer and if the remaining bytes is zero, + // notify the caller of number of bytes written. + entry.notifyObject(entry.getBytesWritten()); + // switch to next write buffer or clear interest bit on socket channel. + if (!entry.next()) { + needToDecrementPendingChannels = true; + } + } + + } + catch (IOException e) { + needToDecrementPendingChannels = true; + // connection must of closed + handleChannelError(entry, e); + } + finally { + if (needToDecrementPendingChannels) + m_pendingChannels--; + } + } + keys.clear(); + } + + public void run() + { + while (selector.isOpen()) + { + try + { + WriteRequest queueEntry; + Object o; + + // When there are no more commands in the Queue, we will hit the blocking code after this loop. + while (null != (o = queue.poll(0L, TimeUnit.MILLISECONDS))) + { + if (o instanceof Shutdown) // Stop the thread + { + try { + selector.close(); + } catch(IOException e) { + if (log.isTraceEnabled()) log.trace("Write selector close operation failed" , e); + } + return; + } + queueEntry = (WriteRequest) o; + + if (queueEntry.getHandler().add(queueEntry)) + { + // If the add operation returns true, than means that a buffer is available to be written to the + // corresponding channel and channel's selection key has been modified to indicate interest in the + // 'write' operation. + // If the add operation threw an exception, we will not increment m_pendingChannels which + // seems correct as long as a new buffer wasn't added to be sent. + // Another way to view this is that we don't have to protect m_pendingChannels on the increment + // side, only need to protect on the decrement side (this logic of this run() will be incorrect + // if m_pendingChannels is set incorrectly). + m_pendingChannels++; + } + + try + { + // process any connections ready to be written to. + if (selector.selectNow() > 0) + { + processWrite(selector); + } + } + catch (IOException e) + { // need to understand what causes this error so we can handle it properly + if (log.isErrorEnabled()) log.error("SelectNow operation on write selector failed, didn't expect this to occur, please report this", e); + return; // if select fails, give up so we don't go into a busy loop. + } + } + + // if there isn't any pending work to do, block on queue to get next request. + if (m_pendingChannels == 0) + { + o = queue.take(); + if (o instanceof Shutdown){ // Stop the thread + try { + selector.close(); + } catch(IOException e) { + if (log.isTraceEnabled()) log.trace("Write selector close operation failed" , e); + } + return; + } + queueEntry = (WriteRequest) o; + if (queueEntry.getHandler().add(queueEntry)) + m_pendingChannels++; + } + // otherwise do a blocking wait select operation. + else + { + try + { + if ((selector.select()) > 0) + { + processWrite(selector); + } + } + catch (IOException e) + { // need to understand what causes this error + if (log.isErrorEnabled()) log.error("Failure while writing to socket",e); + } + } + } + catch (InterruptedException e) + { + if (log.isErrorEnabled()) log.error("Thread ("+Thread.currentThread().getName() +") was interrupted", e); + } + catch (Throwable e) // Log throwable rather than terminating this thread. + { // We are a daemon thread so we shouldn't prevent the process from terminating if + // the controlling thread decides that should happen. + if (log.isErrorEnabled()) log.error("Thread ("+Thread.currentThread().getName() +") caught Throwable" , e); + } + } + } + } + + + // Wrapper class for passing Write requests. There will be an instance of this class for each socketChannel + // mapped to a Selector. + public static class SelectorWriteHandler { + + private final List m_writeRequests = new LinkedList(); // Collection of writeRequests + private boolean m_headerSent = false; + private SocketChannel m_channel; + private SelectionKey m_key; + private Selector m_selector; + private int m_bytesWritten = 0; + private boolean m_enabled = false; + private ByteBuffer m_headerBuffer; + + SelectorWriteHandler(SocketChannel channel, Selector selector, ByteBuffer headerBuffer) + { + m_channel = channel; + m_selector = selector; + m_headerBuffer = headerBuffer; + } + + private void register(Selector selector, SocketChannel channel) throws ClosedChannelException + { + // register the channel but don't enable OP_WRITE until we have a write request. + m_key = channel.register(selector, 0, this); + } + + // return true if selection key is enabled when it wasn't previous to call. + private boolean enable() + { + boolean rc = false; + + try + { + if (m_key == null) + { // register the socket on first access, + // we are the only thread using this variable, so no sync needed. + register(m_selector, m_channel); + } + } + catch (ClosedChannelException e) + { + return rc; + } + + if (!m_enabled) + { + rc = true; + try + { + m_key.interestOps(SelectionKey.OP_WRITE); + } + catch (CancelledKeyException e) + { // channel must of closed + return false; + } + m_enabled = true; + } + return rc; + } + + private void disable() + { + if (m_enabled) + { + try + { + m_key.interestOps(0); // pass zero which means that we are not interested in being + // notified of anything for this channel. + } + catch (CancelledKeyException eat) // If we finished writing and didn't get an exception, then + { // we probably don't need to throw this exception (if they try to write + // again, we will then throw an exception). + } + m_enabled = false; + } + } + + private void cancel() + { + m_key.cancel(); + } + + boolean add(WriteRequest entry) + { + m_writeRequests.add(entry); + return enable(); + } + + WriteRequest getCurrentRequest() + { + return m_writeRequests.get(0); + } + + SocketChannel getChannel() + { + return m_channel; + } + + ByteBuffer getBuffer() + { + return getCurrentRequest().getBuffer(); + } + + MyFuture getCallback() + { + return getCurrentRequest().getCallback(); + } + + int getBytesWritten() + { + return m_bytesWritten; + } + + void notifyError(Throwable error) + { + if (getCallback() != null) + getCallback().setException(error); + } + + void notifyObject(Object result) + { + if (getCallback() != null) + getCallback().set(result); + } + + /** + * switch to next request or disable write interest bit if there are no more buffers. + * + * @return true if another request was found to be processed. + */ + boolean next() + { + m_headerSent = false; + m_bytesWritten = 0; + + m_writeRequests.remove(0); // remove current entry + boolean rc = !m_writeRequests.isEmpty(); + if (!rc) // disable select for this channel if no more entries + disable(); + return rc; + } + + /** + * @return bytes remaining to write. This function will only throw IOException, unchecked exceptions are not + * expected to be thrown from here. It is very important for the caller to know if an unchecked exception can + * be thrown in here. Please correct the following throws list to include any other exceptions and update + * caller to handle them. + * @throws IOException + */ + int write() throws IOException + { + // Send header first. Note that while we are writing the shared header buffer, + // no other threads can access the header buffer as we are the only thread that has access to it. + if (!m_headerSent) + { + m_headerSent = true; + m_headerBuffer.clear(); + m_headerBuffer.putInt(getBuffer().remaining()); + m_headerBuffer.flip(); + do + { + getChannel().write(m_headerBuffer); + } // we should be able to handle writing the header in one action but just in case, just do a busy loop + while (m_headerBuffer.remaining() > 0); + + } + + m_bytesWritten += (getChannel().write(getBuffer())); + + return getBuffer().remaining(); + } + + } + + public static class WriteRequest { + private final SocketChannel m_channel; + private final ByteBuffer m_buffer; + private final MyFuture m_callback; + private final SelectorWriteHandler m_hdlr; + + WriteRequest(SocketChannel channel, ByteBuffer buffer, MyFuture callback, SelectorWriteHandler hdlr) + { + m_channel = channel; + m_buffer = buffer; + m_callback = callback; + m_hdlr = hdlr; + } + + SelectorWriteHandler getHandler() + { + return m_hdlr; + } + + SocketChannel getChannel() + { + return m_channel; + } + + ByteBuffer getBuffer() + { + return m_buffer; + } + + MyFuture getCallback() + { + return m_callback; + } + + } + + private static class NullCallable implements Callable { + + public Object call() { + System.out.println("nullCallable.call invoked"); + return null; + } + } + private static final NullCallable NULLCALL = new NullCallable(); + + public static class MyFuture extends FutureTask { // make FutureTask work like the old FutureResult + public MyFuture() { + super(NULLCALL); + } + + protected void set(Object o) { + super.set(o); + } + + + protected void setException(Throwable t) { + super.setException(t); + } + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/blocks/DistributedHashtable.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/blocks/DistributedHashtable.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/blocks/DistributedHashtable.java 17 Aug 2012 14:51:18 -0000 1.1 @@ -0,0 +1,701 @@ +// $Id: DistributedHashtable.java,v 1.1 2012/08/17 14:51:18 marcin Exp $ + +package org.jgroups.blocks; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.*; +import org.jgroups.persistence.CannotPersistException; +import org.jgroups.persistence.CannotRemoveException; +import org.jgroups.persistence.PersistenceFactory; +import org.jgroups.persistence.PersistenceManager; +import org.jgroups.util.Promise; +import org.jgroups.util.Util; + +import java.io.*; +import java.util.*; + + +/** + * Provides the abstraction of a java.util.Hashtable that is replicated at several + * locations. Any change to the hashtable (clear, put, remove etc) will transparently be + * propagated to all replicas in the group. All read-only methods will always access the + * local replica.

+ * Both keys and values added to the hashtable must be serializable, the reason + * being that they will be sent across the network to all replicas of the group. Having said + * this, it is now for example possible to add RMI remote objects to the hashtable as they + * are derived from java.rmi.server.RemoteObject which in turn is serializable. + * This allows to lookup shared distributed objects by their name and invoke methods on them, + * regardless of one's onw location. A DistributedHashtable thus allows to + * implement a distributed naming service in just a couple of lines.

+ * An instance of this class will contact an existing member of the group to fetch its + * initial state (using the state exchange funclet StateExchangeFunclet. + * @author Bela Ban + * @author Alfonso Olias-Sanz + * @version $Id: DistributedHashtable.java,v 1.1 2012/08/17 14:51:18 marcin Exp $ + * @deprecated Use {@link org.jgroups.blocks.ReplicatedHashMap} instead + */ +public class DistributedHashtable extends Hashtable implements ExtendedMessageListener, ExtendedMembershipListener { + private static final long serialVersionUID=7910133360803785134L; + + + public interface Notification { + void entrySet(Object key, Object value); + void entryRemoved(Object key); + void viewChange(Vector new_mbrs, Vector old_mbrs); + void contentsSet(Map new_entries); + void contentsCleared(); + } + + + private transient Channel channel; + protected transient RpcDispatcher disp=null; + private String groupname=null; + private final transient Vector notifs=new Vector(); // to be notified when mbrship changes + private final Vector members=new Vector(); // keeps track of all DHTs + private transient Class[] put_signature=null; + private transient Class[] putAll_signature=null; + private transient Class[] clear_signature=null; + private transient Class[] remove_signature=null; + private transient boolean persistent=false; // whether to use PersistenceManager to save state + private transient PersistenceManager persistence_mgr=null; + + /** Determines when the updates have to be sent across the network, avoids sending unnecessary + * messages when there are no member in the group */ + private transient boolean send_message = false; + + protected final transient Promise state_promise=new Promise(); + + protected final Log log=LogFactory.getLog(this.getClass()); + + + + + /** + * Creates a DistributedHashtable + * @param groupname The name of the group to join + * @param factory The ChannelFactory which will be used to create a channel + * @param properties The property string to be used to define the channel. This will override the properties of + * the factory. If null, then the factory properties will be used + * @param state_timeout The time to wait until state is retrieved in milliseconds. A value of 0 means wait forever. + */ + public DistributedHashtable(String groupname, ChannelFactory factory, + String properties, long state_timeout) + throws ChannelException { + this.groupname=groupname; + initSignatures(); + if(factory != null) { + channel=properties != null? factory.createChannel((Object)properties) : factory.createChannel(); + } + else { + channel=new JChannel(properties); + } + disp=new RpcDispatcher(channel, this, this, this); + channel.connect(groupname); + start(state_timeout); + } + + /** + * Creates a DisttributedHashtable. Optionally the contents can be saved to + * persistemt storage using the {@link PersistenceManager}. + * @param groupname Name of the group to join + * @param factory Instance of a ChannelFactory to create the channel + * @param properties Protocol stack properties. This will override the properties of the factory. If + * null, then the factory properties will be used + * @param persistent Whether the contents should be persisted + * @param state_timeout Max number of milliseconds to wait until state is + * retrieved + */ + public DistributedHashtable(String groupname, ChannelFactory factory, String properties, + boolean persistent, long state_timeout) + throws ChannelException { + this.groupname=groupname; + this.persistent=persistent; + initSignatures(); + if(factory != null) { + channel=properties != null? factory.createChannel((Object)properties) : factory.createChannel(); + } + else { + channel=new JChannel(properties); + } + disp=new RpcDispatcher(channel, this, this, this); + channel.connect(groupname); + start(state_timeout); + } + + + public DistributedHashtable(Channel channel, long state_timeout) { + this(channel, false, state_timeout); + } + + + public DistributedHashtable(Channel channel, boolean persistent, long state_timeout) { + this.groupname = channel.getClusterName(); + this.channel = channel; + this.persistent=persistent; + init(state_timeout); + } + + /** + * Uses a user-provided PullPushAdapter to create the dispatcher rather than a Channel. If id is non-null, it will be + * used to register under that id. This is typically used when another building block is already using + * PullPushAdapter, and we want to add this building block in addition. The id is the used to discriminate + * between messages for the various blocks on top of PullPushAdapter. If null, we will assume we are the + * first block created on PullPushAdapter. + * @param adapter The PullPushAdapter which to use as underlying transport + * @param id A serializable object (e.g. an Integer) used to discriminate (multiplex/demultiplex) between + * requests/responses for different building blocks on top of PullPushAdapter. + * @param state_timeout Max number of milliseconds to wait until state is + * retrieved + */ + public DistributedHashtable(PullPushAdapter adapter, Serializable id, long state_timeout) + throws ChannelNotConnectedException, ChannelClosedException { + initSignatures(); + this.channel = (Channel)adapter.getTransport(); + this.groupname = this.channel.getClusterName(); + disp=new RpcDispatcher(adapter, id, this, this, this); + start(state_timeout); + } + + public DistributedHashtable(PullPushAdapter adapter, Serializable id) { + initSignatures(); + this.channel = (Channel)adapter.getTransport(); + this.groupname = this.channel.getClusterName(); + disp=new RpcDispatcher(adapter, id, this, this, this); + } + + protected final void init(long state_timeout) { + initSignatures(); + disp = new RpcDispatcher(channel, this, this, this); + + // Changed by bela (jan 20 2003): start() has to be called by user (only when providing + // own channel). First, Channel.connect() has to be called, then start(). + // start(state_timeout); + } + + + /** + * Fetches the state + * @param state_timeout + * @throws ChannelClosedException + * @throws ChannelNotConnectedException + */ + public final void start(long state_timeout) throws ChannelClosedException, ChannelNotConnectedException { + boolean rc; + if(persistent) { + if(log.isInfoEnabled()) log.info("fetching state from database"); + try { + persistence_mgr=PersistenceFactory.getInstance().createManager(); + } + catch(Throwable ex) { + if(log.isErrorEnabled()) log.error("failed creating PersistenceManager, " + + "turning persistency off. Exception: " + Util.printStackTrace(ex)); + persistent=false; + } + } + + state_promise.reset(); + rc=channel.getState(null, state_timeout); + if(rc) { + if(log.isInfoEnabled()) log.info("state was retrieved successfully, waiting for setState()"); + Boolean result=state_promise.getResult(state_timeout); + if(result == null) { + if(log.isErrorEnabled()) log.error("setState() never got called"); + } + else { + if(log.isInfoEnabled()) log.info("setState() was called"); + } + } + else { + if(log.isInfoEnabled()) log.info("state could not be retrieved (first member)"); + if(persistent) { + if(log.isInfoEnabled()) log.info("fetching state from database"); + try { + Map m=persistence_mgr.retrieveAll(); + if(m != null) { + Map.Entry entry; + Object key, val; + for(Iterator it=m.entrySet().iterator(); it.hasNext();) { + entry=(Map.Entry)it.next(); + key=entry.getKey(); + val=entry.getValue(); + if(log.isInfoEnabled()) log.info("inserting " + key + " --> " + val); + put(key, val); // will replicate key and value + } + } + } + catch(Throwable ex) { + if(log.isErrorEnabled()) log.error("failed creating PersistenceManager, " + + "turning persistency off. Exception: " + Util.printStackTrace(ex)); + persistent=false; + } + } + } + } + + + public Address getLocalAddress() {return channel != null ? channel.getLocalAddress() : null;} + public String getGroupName() {return groupname;} + public Channel getChannel() {return channel;} + public boolean getPersistent() {return persistent;} + public void setPersistent(boolean p) {persistent=p;} + + + public void setDeadlockDetection(boolean flag) { + if(disp != null) + disp.setDeadlockDetection(flag); + } + + public void addNotifier(Notification n) { + if(!notifs.contains(n)) + notifs.addElement(n); + } + + public void removeNotifier(Notification n) { + if(notifs.contains(n)) + notifs.removeElement(n); + } + + public void stop() { + if(disp != null) { + disp.stop(); + disp=null; + } + if(channel != null) { + channel.close(); + channel=null; + } + } + + + /** + * Maps the specified key to the specified value in the hashtable. Neither of both parameters can be null + * @param key - the hashtable key + * @param value - the value + * @return the previous value of the specified key in this hashtable, or null if it did not have one + */ + public Object put(Object key, Object value) { + Object prev_val=get(key); + + //Changes done by + //if true, propagate action to the group + if(send_message == true) { + try { + disp.callRemoteMethods( + null, "_put", new Object[]{key,value}, + put_signature, + GroupRequest.GET_ALL, + 0); + } + catch(Exception e) { + //return null; + } + } + else { + _put(key, value); + //don't have to do prev_val = super.put(..) as is done at the beginning + } + return prev_val; + } + + /** + * Copies all of the mappings from the specified Map to this Hashtable These mappings will replace any mappings that this Hashtable had for any of the keys currently in the specified Map. + * @param m - Mappings to be stored in this map + */ + public void putAll(Map m) { + //Changes done by + //if true, propagate action to the group + if(send_message == true) { + try { + disp.callRemoteMethods( + null, "_putAll", new Object[]{m}, + putAll_signature, + GroupRequest.GET_ALL, + 0); + } + catch(Throwable t) { + } + } + else { + _putAll(m); + } + } + + /** + * Clears this hashtable so that it contains no keys + */ + public void clear() { + //Changes done by + //if true, propagate action to the group + if(send_message == true) { + try { + disp.callRemoteMethods( + null, "_clear", null, + clear_signature, + GroupRequest.GET_ALL, + 0); + } + catch(Exception e) { + if(log.isErrorEnabled()) log.error("exception=" + e); + } + } + else { + _clear(); + } + } + + /** + * Removes the key (and its corresponding value) from the Hashtable. + * @param key - the key to be removed. + * @return the value to which the key had been mapped in this hashtable, or null if the key did not have a mapping. + */ + public Object remove(Object key) { + Object retval = get(key); + + //Changes done by + //if true, propagate action to the group + if(send_message == true) { + try { + disp.callRemoteMethods( + null, "_remove", new Object[]{key}, + remove_signature, + GroupRequest.GET_ALL, + 0); + //return retval; + } + catch(Exception e) { + //return null; + } + } + else { + _remove(key); + //don't have to do retval = super.remove(..) as is done at the beginning + } + return retval; + } + + + + /*------------------------ Callbacks -----------------------*/ + + public Object _put(Object key, Object value) { + Object retval=super.put(key, value); + if(persistent) { + try { + persistence_mgr.save((Serializable)key, (Serializable)value); + } + catch(CannotPersistException cannot_persist_ex) { + if(log.isErrorEnabled()) log.error("failed persisting " + key + " + " + + value + ", exception=" + cannot_persist_ex); + } + catch(Throwable t) { + if(log.isErrorEnabled()) log.error("failed persisting " + key + " + " + + value + ", exception=" + Util.printStackTrace(t)); + } + } + for(int i=0; i < notifs.size(); i++) + ((Notification)notifs.elementAt(i)).entrySet(key, value); + return retval; + } + + + /** + * @see java.util.Map#putAll(java.util.Map) + */ + public void _putAll(Map m) { + if (m == null) + return; + + // Calling the method below seems okay, but would result in ... deadlock ! + // The reason is that Map.putAll() calls put(), which we override, which results in + // lock contention for the map. + + // ---> super.putAll(m); <--- CULPRIT !!!@#$%$ + + // That said let's do it the stupid way: + Map.Entry entry; + for(Iterator it=m.entrySet().iterator(); it.hasNext();) { + entry=(Map.Entry)it.next(); + super.put(entry.getKey(), entry.getValue()); + } + + if (persistent) { + try { + persistence_mgr.saveAll(m); + } + catch (CannotPersistException persist_ex) { + if(log.isErrorEnabled()) log.error("failed persisting contents: " + persist_ex); + } + catch (Throwable t) { + if(log.isErrorEnabled()) log.error("failed persisting contents: " + t); + } + } + for(int i=0; i < notifs.size(); i++) + ((Notification)notifs.elementAt(i)).contentsSet(m); + } + + + public void _clear() { + super.clear(); + if(persistent) { + try { + persistence_mgr.clear(); + } + catch(CannotRemoveException cannot_remove_ex) { + if(log.isErrorEnabled()) log.error("failed clearing contents, exception=" + cannot_remove_ex); + } + catch(Throwable t) { + if(log.isErrorEnabled()) log.error("failed clearing contents, exception=" + t); + } + } + for(int i=0; i < notifs.size(); i++) + ((Notification)notifs.elementAt(i)).contentsCleared(); + } + + + public Object _remove(Object key) { + Object retval=super.remove(key); + if(persistent) { + try { + persistence_mgr.remove((Serializable)key); + } + catch(CannotRemoveException cannot_remove_ex) { + if(log.isErrorEnabled()) log.error("failed clearing contents, exception=" + cannot_remove_ex); + } + catch(Throwable t) { + if(log.isErrorEnabled()) log.error("failed clearing contents, exception=" + t); + } + } + for(int i=0; i < notifs.size(); i++) + ((Notification)notifs.elementAt(i)).entryRemoved(key); + + return retval; + } + + /*----------------------------------------------------------*/ + + + + /*-------------------- State Exchange ----------------------*/ + + public void receive(Message msg) { } + + public byte[] getState() { + Object key, val; + Hashtable copy=new Hashtable(); + + for(Enumeration e=keys(); e.hasMoreElements();) { + key=e.nextElement(); + val=get(key); + copy.put(key, val); + } + try { + return Util.objectToByteBuffer(copy); + } + catch(Throwable ex) { + if(log.isErrorEnabled()) log.error("exception marshalling state: " + ex); + return null; + } + } + + + public void setState(byte[] new_state) { + Hashtable new_copy; + + try { + new_copy=(Hashtable)Util.objectFromByteBuffer(new_state); + if(new_copy == null) + return; + } + catch(Throwable ex) { + if(log.isErrorEnabled()) log.error("exception unmarshalling state: " + ex); + return; + } + _putAll(new_copy); + state_promise.setResult(Boolean.TRUE); + } + + + + /*------------------- Membership Changes ----------------------*/ + + public void viewAccepted(View new_view) { + Vector new_mbrs=new_view.getMembers(); + + if(new_mbrs != null) { + sendViewChangeNotifications(new_mbrs, members); // notifies observers (joined, left) + members.removeAllElements(); + for(int i=0; i < new_mbrs.size(); i++) + members.addElement(new_mbrs.elementAt(i)); + } + //if size is bigger than one, there are more peers in the group + //otherwise there is only one server. + send_message=members.size() > 1; + } + + + /** Called when a member is suspected */ + public void suspect(Address suspected_mbr) { + ; + } + + + /** Block sending and receiving of messages until ViewAccepted is called */ + public void block() {} + + + + void sendViewChangeNotifications(Vector new_mbrs, Vector old_mbrs) { + Vector joined, left; + Object mbr; + Notification n; + + if(notifs.size() == 0 || old_mbrs == null || new_mbrs == null || + old_mbrs.size() == 0 || new_mbrs.size() == 0) + return; + + + // 1. Compute set of members that joined: all that are in new_mbrs, but not in old_mbrs + joined=new Vector(); + for(int i=0; i < new_mbrs.size(); i++) { + mbr=new_mbrs.elementAt(i); + if(!old_mbrs.contains(mbr)) + joined.addElement(mbr); + } + + + // 2. Compute set of members that left: all that were in old_mbrs, but not in new_mbrs + left=new Vector(); + for(int i=0; i < old_mbrs.size(); i++) { + mbr=old_mbrs.elementAt(i); + if(!new_mbrs.contains(mbr)) { + left.addElement(mbr); + } + } + + for(int i=0; i < notifs.size(); i++) { + n=(Notification)notifs.elementAt(i); + n.viewChange(joined, left); + } + } + + + final void initSignatures() { + try { + if(put_signature == null) { + put_signature=new Class[] {Object.class,Object.class}; + } + + if(putAll_signature == null) { + putAll_signature=new Class[] {Map.class}; + } + + if(clear_signature == null) + clear_signature=new Class[0]; + + if(remove_signature == null) { + remove_signature=new Class[] {Object.class}; + } + } + catch(Throwable ex) { + if(log.isErrorEnabled()) log.error("exception=" + ex); + } + } + + public static void main(String[] args) { + try { + // The setup here is kind of weird: + // 1. Create a channel + // 2. Create a DistributedHashtable (on the channel) + // 3. Connect the channel (so the HT gets a VIEW_CHANGE) + // 4. Start the HT + // + // A simpler setup is + // DistributedHashtable ht = new DistributedHashtable("demo", null, + // "file://c:/JGroups-2.0/conf/state_transfer.xml", 5000); + + JChannel c = new JChannel("file:/c:/JGroups-2.0/conf/state_transfer.xml"); + DistributedHashtable ht = new DistributedHashtable(c, false, 5000); + c.connect("demo"); + ht.start(5000); + + + + ht.put("name", "Michelle Ban"); + Object old_key = ht.remove("name"); + System.out.println("old key was " + old_key); + ht.put("newkey", "newvalue"); + + Map m = new HashMap(); + m.put("k1", "v1"); + m.put("k2", "v2"); + + ht.putAll(m); + + System.out.println("hashmap is " + ht); + } + catch (Throwable t) { + t.printStackTrace(); + } + } + + public byte[] getState(String state_id) { + // not implemented + return null; + } + + public void getState(OutputStream ostream) { + Object key, val; + Hashtable copy=new Hashtable(); + ObjectOutputStream oos = null; + + for(Enumeration e=keys(); e.hasMoreElements();) { + key=e.nextElement(); + val=get(key); + copy.put(key, val); + } + try { + oos = new ObjectOutputStream(ostream); + oos.writeObject(copy); + } + catch(Throwable ex) { + if(log.isErrorEnabled()) log.error("exception marshalling state: " + ex); + } + finally{ + Util.close(oos); + } + } + + public void getState(String state_id, OutputStream ostream) { + } + + public void setState(String state_id, byte[] state) { + } + + public void setState(InputStream istream) { + Hashtable new_copy = null; + ObjectInputStream ois = null; + try{ + ois = new ObjectInputStream(istream); + new_copy = (Hashtable) ois.readObject(); + ois.close(); + }catch(Throwable e){ + e.printStackTrace(); + if(log.isErrorEnabled()) log.error("exception marshalling state: " + e); + }finally{ + Util.close(ois); + } + if(new_copy != null) + _putAll(new_copy); + + state_promise.setResult(Boolean.TRUE); + } + + public void setState(String state_id, InputStream istream) { + } + + public void unblock() { + } + +} + Index: 3rdParty_sources/jgroups/org/jgroups/blocks/DistributedLockManager.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/blocks/DistributedLockManager.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/blocks/DistributedLockManager.java 17 Aug 2012 14:51:17 -0000 1.1 @@ -0,0 +1,784 @@ +package org.jgroups.blocks; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.ChannelException; +import org.jgroups.MembershipListener; +import org.jgroups.View; +import org.jgroups.Address; +import org.jgroups.blocks.VotingAdapter.FailureVoteResult; +import org.jgroups.blocks.VotingAdapter.VoteResult; +import org.jgroups.util.Rsp; +import org.jgroups.util.RspList; + +import java.io.Serializable; +import java.util.*; + +/** + * Distributed lock manager is responsible for maintaining the lock information + * consistent on all participating nodes. + * + * @author Roman Rokytskyy (rrokytskyy@acm.org) + * @author Robert Schaffar-Taurok (robert@fusion.at) + * @version $Id: DistributedLockManager.java,v 1.1 2012/08/17 14:51:17 marcin Exp $ + */ +public class DistributedLockManager implements TwoPhaseVotingListener, LockManager, VoteResponseProcessor, MembershipListener { + /** + * Definitions for the implementation of the VoteResponseProcessor + */ + private static final int PROCESS_CONTINUE = 0; + private static final int PROCESS_SKIP = 1; + private static final int PROCESS_BREAK = 2; + + /** + * This parameter means that lock acquisition expires after 5 seconds. + * If there were no "commit" operation on prepared lock, then it + * is treated as expired and is removed from the prepared locks table. + */ + private static final long ACQUIRE_EXPIRATION = 5000; + + /** + * This parameter is used during lock releasing. If group fails to release + * the lock during the specified period of time, unlocking fails. + */ + private static final long VOTE_TIMEOUT = 10000; + + /** HashMap. List of all prepared locks */ + private final HashMap preparedLocks = new HashMap(); + + /** HashMap. List of all prepared releases */ + private final HashMap preparedReleases = new HashMap(); + + /* HashMap. List of locks on the node */ + private final HashMap heldLocks = new HashMap(); + + private final TwoPhaseVotingAdapter votingAdapter; + + private final Object id; + + final Vector current_members=new Vector(); + + protected final Log log=LogFactory.getLog(getClass()); + + + /** + * Create instance of this class. + * + * @param voteChannel instance of {@link VotingAdapter} that will be used + * for voting purposes on the lock decrees. voteChannel() will + * be wrapped by the instance of the {@link TwoPhaseVotingAdapter}. + * + * @param id the unique identifier of this lock manager. + * + * todo check if the node with the same id is already in the group. + */ + public DistributedLockManager(VotingAdapter voteChannel, Object id) { + this(new TwoPhaseVotingAdapter(voteChannel), id); + } + + /** + * Constructor for the DistributedLockManager_cl object. + * + * @param channel instance of {@link TwoPhaseVotingAdapter} + * that will be used for voting purposes on the lock decrees. + * + * @param id the unique identifier of this lock manager. + * + * @todo check if the node with the same id is already in the group. + */ + public DistributedLockManager(TwoPhaseVotingAdapter channel, Object id) { + this.id = id; + this.votingAdapter = channel; + this.votingAdapter.addListener(this); + if(votingAdapter.getVoteChannel() != null) { + votingAdapter.getVoteChannel().addMembershipListener(this); + setInitialMembership(votingAdapter.getVoteChannel().getMembers()); + } + } + + private void setInitialMembership(Collection members) { + if(members != null) { + current_members.clear(); + current_members.addAll(members); + } + } + + + /** + * Performs local lock. This method also performs the clean-up of the lock + * table, all expired locks are removed. + */ + private boolean localLock(LockDecree lockDecree) { + // remove expired locks + removeExpired(lockDecree); + + LockDecree localLock = + (LockDecree) heldLocks.get(lockDecree.getKey()); + + if (localLock == null) { + + // promote lock into commited state + lockDecree.commit(); + + // no lock exist, perform local lock, note: + // we do not store locks that were requested by other manager. + if (lockDecree.managerId.equals(id)) + heldLocks.put(lockDecree.getKey(), lockDecree); + + // everything is fine :) + return true; + } else + return localLock.requester.equals(lockDecree.requester); + + } + + /** + * Returns true if the requested lock can be granted by the + * current node. + * + * @param decree instance of LockDecree containing information + * about the lock. + */ + private boolean canLock(LockDecree decree) { + // clean expired locks + removeExpired(decree); + + LockDecree lock = (LockDecree)heldLocks.get(decree.getKey()); + return lock == null || lock.requester.equals(decree.requester); + } + + /** + * Returns true if the requested lock can be released by the + * current node. + * + * @param decree instance of {@link LockDecree} containing information + * about the lock. + */ + private boolean canRelease(LockDecree decree) { + // clean expired locks + removeExpired(decree); + + // we need to check only hold locks, because + // prepared locks cannot contain the lock + LockDecree lock = (LockDecree)heldLocks.get(decree.getKey()); + return lock == null || lock.requester.equals(decree.requester); + } + + /** + * Removes expired locks. + * + * @param decree instance of {@link LockDecree} describing the lock. + */ + private void removeExpired(LockDecree decree) { + // remove the invalid (expired) lock + LockDecree localLock = (LockDecree)heldLocks.get(decree.getKey()); + if (localLock != null && !localLock.isValid()) + heldLocks.remove(localLock.getKey()); + } + + /** + * Releases lock locally. + * + * @param lockDecree instance of {@link LockDecree} describing the lock. + */ + private boolean localRelease(LockDecree lockDecree) { + // remove expired locks + removeExpired(lockDecree); + + LockDecree localLock= + (LockDecree) heldLocks.get(lockDecree.getKey()); + + if(localLock == null) { + // no lock exist + return true; + } + else if(localLock.requester.equals(lockDecree.requester)) { + // requester owns the lock, release the lock + heldLocks.remove(lockDecree.getKey()); + return true; + } + else + // lock does not belong to requester + return false; + } + + /** + * Locks an object with lockId on behalf of the specified + * owner. + * + * @param lockId Object representing the object to be locked. + * @param owner object that requests the lock. This should be the Address of a JGroups member, otherwise we cannot + * release the locks for a crashed member ! + * @param timeout time during which group members should decide + * whether to grant a lock or not. + * + * @throws LockNotGrantedException when the lock cannot be granted. + * + * @throws ClassCastException if lockId or owner are not serializable. + * + * @throws ChannelException if something bad happened to underlying channel. + */ + public void lock(Object lockId, Object owner, int timeout) + throws LockNotGrantedException, ChannelException + { + if (!(lockId instanceof Serializable) || !(owner instanceof Serializable)) + throw new ClassCastException("DistributedLockManager works only with serializable objects."); + + boolean acquired = votingAdapter.vote( + new AcquireLockDecree(lockId, owner, id), timeout); + + if (!acquired) + throw new LockNotGrantedException("Lock " + lockId + " cannot be granted."); + } + + /** + * Unlocks an object with lockId on behalf of the specified + * owner. + * + * since 2.2.9 this method is only a wrapper for + * unlock(Object lockId, Object owner, boolean releaseMultiLocked). + * Use that with releaseMultiLocked set to true if you want to be able to + * release multiple locked locks (for example after a merge) + * + * @param lockId long representing the object to be unlocked. + * @param owner object that releases the lock. + * + * @throws LockNotReleasedException when the lock cannot be released. + * @throws ClassCastException if lockId or owner are not serializable. + * + */ + public void unlock(Object lockId, Object owner) + throws LockNotReleasedException, ChannelException + { + try { + unlock(lockId, owner, false, VOTE_TIMEOUT); + } catch (LockMultiLockedException e) { + // This should never happen when releaseMultiLocked is false + log.error("Caught MultiLockedException but releaseMultiLocked is false", e); + } + } + + + public void unlock(Object lockId, Object owner, long timeout) + throws LockNotReleasedException, ChannelException + { + try { + unlock(lockId, owner, false, timeout); + } catch (LockMultiLockedException e) { + // This should never happen when releaseMultiLocked is false + log.error("Caught MultiLockedException but releaseMultiLocked is false", e); + } + } + + + /** + * Unlocks an object with lockId on behalf of the specified + * owner. + * @param lockId long representing the object to be unlocked. + * @param owner object that releases the lock. + * @param releaseMultiLocked releases also multiple locked locks. (eg. locks that are locked by another DLM after a merge) + * + * @throws LockNotReleasedException when the lock cannot be released. + * @throws ClassCastException if lockId or owner are not serializable. + * @throws LockMultiLockedException if releaseMultiLocked is true and a multiple locked lock has been released. + */ + public void unlock(Object lockId, Object owner, boolean releaseMultiLocked) + throws LockNotReleasedException, ChannelException, LockMultiLockedException + { + unlock(lockId, owner, releaseMultiLocked, VOTE_TIMEOUT); + } + + + public void unlock(Object lockId, Object owner, boolean releaseMultiLocked, long timeout) + throws LockNotReleasedException, ChannelException, LockMultiLockedException + { + + if (!(lockId instanceof Serializable) || !(owner instanceof Serializable)) + throw new ClassCastException("DistributedLockManager " + + "works only with serializable objects."); + + ReleaseLockDecree releaseLockDecree = new ReleaseLockDecree(lockId, owner, id); + boolean released = false; + if (releaseMultiLocked) { + released = votingAdapter.vote(releaseLockDecree, timeout, this); + if (releaseLockDecree.isMultipleLocked()) { + throw new LockMultiLockedException("Lock was also locked by other DistributedLockManager(s)"); + } + } else { + released = votingAdapter.vote(releaseLockDecree, timeout); + } + + if (!released) + throw new LockNotReleasedException("Lock cannot be unlocked."); + } + + + + /** + * Checks the list of prepared locks/unlocks to determine if we are in the + * middle of the two-phase commit process for the lock acqusition/release. + * Here we do not tolerate if the request comes from the same node on behalf + * of the same owner. + * + * @param preparedContainer either preparedLocks or + * preparedReleases depending on the situation. + * + * @param requestedDecree instance of LockDecree representing + * the lock. + */ + private static boolean checkPrepared(HashMap preparedContainer, + LockDecree requestedDecree) + { + LockDecree preparedDecree = + (LockDecree)preparedContainer.get(requestedDecree.getKey()); + + // if prepared lock is not valid, remove it from the list + if ((preparedDecree != null) && !preparedDecree.isValid()) { + preparedContainer.remove(preparedDecree.getKey()); + + preparedDecree = null; + } + + return preparedDecree == null || requestedDecree.requester.equals(preparedDecree.requester); + } + + /** + * Prepare phase for the lock acquisition or release. + * + * @param decree should be an instance LockDecree, if not, + * we throw VoteException to be ignored by the + * VoteChannel. + * + * @return true when preparing the lock operation succeeds. + * + * @throws VoteException if we should be ignored during voting. + */ + public synchronized boolean prepare(Object decree) throws VoteException { + if (!(decree instanceof LockDecree)) + throw new VoteException("Uknown decree type. Ignore me."); + + if (decree instanceof AcquireLockDecree) { + AcquireLockDecree acquireDecree = (AcquireLockDecree)decree; + if(log.isDebugEnabled()) log.debug("Preparing to acquire decree " + acquireDecree.lockId); + + if (!checkPrepared(preparedLocks, acquireDecree)) + // there is a prepared lock owned by third party + return false; + + if (canLock(acquireDecree)) { + preparedLocks.put(acquireDecree.getKey(), acquireDecree); + return true; + } else + // we are unable to aquire local lock + return false; + } else + if (decree instanceof ReleaseLockDecree) { + ReleaseLockDecree releaseDecree = (ReleaseLockDecree)decree; + + + if(log.isDebugEnabled()) log.debug("Preparing to release decree " + releaseDecree.lockId); + + if (!checkPrepared(preparedReleases, releaseDecree)) + // there is a prepared release owned by third party + return false; + + if (canRelease(releaseDecree)) { + preparedReleases.put(releaseDecree.getKey(), releaseDecree); + // we have local lock and the prepared lock + return true; + } else + // we were unable to aquire local lock + return false; + } else + if (decree instanceof MultiLockDecree) { + // Here we abuse the voting mechanism for notifying the other lockManagers of multiple locked objects. + MultiLockDecree multiLockDecree = (MultiLockDecree)decree; + + if(log.isDebugEnabled()) { + log.debug("Marking " + multiLockDecree.getKey() + " as multilocked"); + } + + LockDecree lockDecree = (LockDecree)heldLocks.get(multiLockDecree.getKey()); + if (lockDecree != null) { + lockDecree.setMultipleLocked(true); + } + return true; + } + + // we should not be here + return false; + } + + /** + * Commit phase for the lock acquisition or release. + * + * @param decree should be an instance LockDecree, if not, + * we throw VoteException to be ignored by the + * VoteChannel. + * + * @return true when commiting the lock operation succeeds. + * + * @throws VoteException if we should be ignored during voting. + */ + public synchronized boolean commit(Object decree) throws VoteException { + if (!(decree instanceof LockDecree)) + throw new VoteException("Uknown decree type. Ignore me."); + + if (decree instanceof AcquireLockDecree) { + + + if(log.isDebugEnabled()) log.debug("Committing decree acquisition " + ((LockDecree)decree).lockId); + + if (!checkPrepared(preparedLocks, (LockDecree)decree)) + // there is a prepared lock owned by third party + return false; + + if (localLock((LockDecree)decree)) { + preparedLocks.remove(((LockDecree)decree).getKey()); + return true; + } else + return false; + } else + if (decree instanceof ReleaseLockDecree) { + + + if(log.isDebugEnabled()) log.debug("Committing decree release " + ((LockDecree)decree).lockId); + + if (!checkPrepared(preparedReleases, (LockDecree)decree)) + // there is a prepared release owned by third party + return false; + + if (localRelease((LockDecree)decree)) { + preparedReleases.remove(((LockDecree)decree).getKey()); + return true; + } else + return false; + } else + if (decree instanceof MultiLockDecree) { + return true; + } + + // we should not be here + return false; + } + + /** + * Abort phase for the lock acquisition or release. + * + * @param decree should be an instance LockDecree, if not, + * we throw VoteException to be ignored by the + * VoteChannel. + * + * @throws VoteException if we should be ignored during voting. + */ + public synchronized void abort(Object decree) throws VoteException { + if (!(decree instanceof LockDecree)) + throw new VoteException("Uknown decree type. Ignore me."); + + if (decree instanceof AcquireLockDecree) { + + + if(log.isDebugEnabled()) log.debug("Aborting decree acquisition " + ((LockDecree)decree).lockId); + + if (!checkPrepared(preparedLocks, (LockDecree)decree)) + // there is a prepared lock owned by third party + return; + + preparedLocks.remove(((LockDecree)decree).getKey()); + } else + if (decree instanceof ReleaseLockDecree) { + + + if(log.isDebugEnabled()) log.debug("Aborting decree release " + ((LockDecree)decree).lockId); + + if (!checkPrepared(preparedReleases, (LockDecree)decree)) + // there is a prepared release owned by third party + return; + + preparedReleases.remove(((LockDecree)decree).getKey()); + } + + } + + + /** + * Processes the response list and votes like the default processResponses method with the consensusType VOTE_ALL + * If the result of the voting is false, but this DistributedLockManager owns the lock, the result is changed to + * true and the lock is released, but marked as multiple locked. (only in the prepare state to reduce traffic) + *

+ * Note: we do not support voting in case of Byzantine failures, i.e. + * when the node responds with the fault message. + */ + public boolean processResponses(RspList responses, int consensusType, Object decree) throws ChannelException { + if (responses == null) { + return false; + } + + int totalPositiveVotes = 0; + int totalNegativeVotes = 0; + + for (int i = 0; i < responses.size(); i++) { + Rsp response = (Rsp) responses.elementAt(i); + + switch (checkResponse(response)) { + case PROCESS_SKIP: + continue; + case PROCESS_BREAK: + return false; + } + + VoteResult result = (VoteResult) response.getValue(); + + totalPositiveVotes += result.getPositiveVotes(); + totalNegativeVotes += result.getNegativeVotes(); + } + + boolean voteResult = (totalNegativeVotes == 0 && totalPositiveVotes > 0); + + if (decree instanceof TwoPhaseVotingAdapter.TwoPhaseWrapper) { + TwoPhaseVotingAdapter.TwoPhaseWrapper wrappedDecree = (TwoPhaseVotingAdapter.TwoPhaseWrapper)decree; + if (wrappedDecree.isPrepare()) { + Object unwrappedDecree = wrappedDecree.getDecree(); + if (unwrappedDecree instanceof ReleaseLockDecree) { + ReleaseLockDecree releaseLockDecree = (ReleaseLockDecree)unwrappedDecree; + LockDecree lock = null; + if ((lock = (LockDecree)heldLocks.get(releaseLockDecree.getKey())) != null) { + // If there is a local lock... + if (!voteResult) { + // ... and another DLM voted negatively, but this DLM owns the lock + // we inform the other node, that it's lock is multiple locked + if (informLockingNodes(releaseLockDecree)) { + + // we set the local lock to multiple locked + lock.setMultipleLocked(true); + + voteResult = true; + } + } + if (lock.isMultipleLocked()) { + //... and the local lock is marked as multilocked + // we mark the releaseLockDecree als multiple locked for evaluation when unlock returns + releaseLockDecree.setMultipleLocked(true); + } + } + } + } + } + + return voteResult; + } + + /** + * This method checks the response and says the processResponses() method + * what to do. + * @return PROCESS_CONTINUE to continue calculating votes, + * PROCESS_BREAK to stop calculating votes from the nodes, + * PROCESS_SKIP to skip current response. + * @throws ChannelException when the response is fatal to the + * current voting process. + */ + private int checkResponse(Rsp response) throws ChannelException { + + if (!response.wasReceived()) { + + if (log.isDebugEnabled()) + log.debug("Response from node " + response.getSender() + " was not received."); + + throw new ChannelException("Node " + response.getSender() + " failed to respond."); + } + + if (response.wasSuspected()) { + + if (log.isDebugEnabled()) + log.debug("Node " + response.getSender() + " was suspected."); + + return PROCESS_SKIP; + } + + Object object = response.getValue(); + + // we received exception/error, something went wrong + // on one of the nodes... and we do not handle such faults + if (object instanceof Throwable) { + throw new ChannelException("Node " + response.getSender() + " is faulty."); + } + + if (object == null) { + return PROCESS_SKIP; + } + + // it is always interesting to know the class that caused failure... + if (!(object instanceof VoteResult)) { + String faultClass = object.getClass().getName(); + + // ...but we do not handle byzantine faults + throw new ChannelException("Node " + response.getSender() + " generated fault (class " + faultClass + ')'); + } + + // what if we received the response from faulty node? + if (object instanceof FailureVoteResult) { + + if (log.isErrorEnabled()) + log.error(((FailureVoteResult) object).getReason()); + + return PROCESS_BREAK; + } + + // everything is fine :) + return PROCESS_CONTINUE; + } + + private boolean informLockingNodes(ReleaseLockDecree releaseLockDecree) throws ChannelException { + return votingAdapter.vote(new MultiLockDecree(releaseLockDecree), VOTE_TIMEOUT); + } + + /** Remove all locks held by members who left the previous view */ + public void viewAccepted(View new_view) { + Vector prev_view=new Vector(current_members); + current_members.clear(); + current_members.addAll(new_view.getMembers()); + + if(log.isDebugEnabled()) + log.debug("-- VIEW: " + current_members + ", old view: " + prev_view); + + prev_view.removeAll(current_members); + if(!prev_view.isEmpty()) { // we have left members, so we need to check for locks which are still held by them + for(Iterator it=prev_view.iterator(); it.hasNext();) { + Object mbr=it.next(); + removeLocksHeldBy(preparedLocks, mbr); + removeLocksHeldBy(preparedReleases, mbr); + removeLocksHeldBy(heldLocks, mbr); + } + } + } + + /** Remove from preparedLocks, preparedReleases and heldLocks */ + private void removeLocksHeldBy(Map lock_table, Object mbr) { + Map.Entry entry; + LockDecree val; + Object holder; + for(Iterator it=lock_table.entrySet().iterator(); it.hasNext();) { + entry=(Map.Entry)it.next(); + val=(LockDecree)entry.getValue(); + holder=val.requester; + if(holder != null && holder.equals(mbr)) { + if(log.isTraceEnabled()) + log.trace("removing a leftover lock held by " + mbr + " for " + entry.getKey() + ": " + val); + it.remove(); + } + } + } + + public void suspect(Address suspected_mbr) { + } + + public void block() { + } + + /** + * This class represents the lock + */ + public static class LockDecree implements Serializable { + + protected final Object lockId; + protected final Object requester; + protected final Object managerId; + + protected boolean commited; + + private boolean multipleLocked = false; + private static final long serialVersionUID = 7264104838035219212L; + + private LockDecree(Object lockId, Object requester, Object managerId) { + this.lockId = lockId; + this.requester = requester; + this.managerId = managerId; + } + + /** + * Returns the key that should be used for Map lookup. + */ + public Object getKey() { return lockId; } + + /** + * This is a place-holder for future lock expiration code. + */ + public boolean isValid() { return true; } + + public void commit() { this.commited = true; } + + /** + * @return Returns the multipleLocked. + */ + public boolean isMultipleLocked() { + return multipleLocked; + } + /** + * @param multipleLocked The multipleLocked to set. + */ + public void setMultipleLocked(boolean multipleLocked) { + this.multipleLocked = multipleLocked; + } + /** + * This is hashcode from the java.lang.Long class. + */ + public int hashCode() { + return lockId.hashCode(); + } + + public boolean equals(Object other) { + + return other instanceof LockDecree && ((LockDecree)other).lockId.equals(this.lockId); + } + } + + + /** + * This class represents the lock to be released. + */ + public static class AcquireLockDecree extends LockDecree { + private final long creationTime; + + private AcquireLockDecree(Object lockId, Object requester, Object managerId) { + super(lockId, requester, managerId); + this.creationTime = System.currentTimeMillis(); + } + + /** + * Lock aquire decree is valid for a ACQUIRE_EXPIRATION + * time after creation and if the lock is still valid (in the + * future locks will be leased for a predefined period of time). + */ + public boolean isValid() { + boolean result = super.isValid(); + + if (!commited && result) + result = ((creationTime + ACQUIRE_EXPIRATION) > System.currentTimeMillis()); + + return result; + } + + } + + /** + * This class represents the lock to be released. + */ + public static class ReleaseLockDecree extends LockDecree { + ReleaseLockDecree(Object lockId, Object requester, Object managerId) { + super(lockId, requester, managerId); + } + } + + /** + * This class represents the lock that has to be marked as multilocked + */ + public static class MultiLockDecree extends LockDecree { + MultiLockDecree(Object lockId, Object requester, Object managerId) { + super(lockId, requester, managerId); + } + + MultiLockDecree(ReleaseLockDecree releaseLockDecree) { + super(releaseLockDecree.lockId, releaseLockDecree.requester, releaseLockDecree.managerId); + } + } +} Index: 3rdParty_sources/jgroups/org/jgroups/blocks/DistributedQueue.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/blocks/DistributedQueue.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/blocks/DistributedQueue.java 17 Aug 2012 14:51:18 -0000 1.1 @@ -0,0 +1,732 @@ +// $Id: DistributedQueue.java,v 1.1 2012/08/17 14:51:18 marcin Exp $ +package org.jgroups.blocks; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.*; +import org.jgroups.util.RspList; +import org.jgroups.util.Util; + +import java.io.Serializable; +import java.util.*; + + +/** + * Provides the abstraction of a java.util.LinkedList that is replicated at several + * locations. Any change to the list (reset, add, remove, etc.) will transparently be + * propagated to all replicas in the group. All read-only methods will always access the + * local replica.

+ * Both keys and values added to the list must be serializable, the reason + * being that they will be sent across the network to all replicas of the group. + * An instance of this class will contact an existing member of the group to fetch its + * initial state. + * Beware to use a total protocol on initialization or elements would not be in same + * order on all replicas. + * @author Romuald du Song + */ +public class DistributedQueue implements MessageListener, MembershipListener, Cloneable +{ + public interface Notification + { + void entryAdd(Object value); + + void entryRemoved(Object key); + + void viewChange(Vector new_mbrs, Vector old_mbrs); + + void contentsCleared(); + + void contentsSet(Collection new_entries); + } + + protected Log logger = LogFactory.getLog(getClass()); + private long internal_timeout = 10000; // 10 seconds to wait for a response + + /*lock object for synchronization*/ + protected final Object mutex = new Object(); + protected boolean stopped = false; // whether to we are stopped ! + protected LinkedList internalQueue; + protected Channel channel; + protected RpcDispatcher disp = null; + protected String groupname = null; + protected Vector notifs = new Vector(); // to be notified when mbrship changes + protected Vector members = new Vector(); // keeps track of all DHTs + private Class[] add_signature = null; + private Class[] addAtHead_signature = null; + private Class[] addAll_signature = null; + private Class[] reset_signature = null; + private Class[] remove_signature = null; + + /** + * Creates a DistributedQueue + * @param groupname The name of the group to join + * @param factory The ChannelFactory which will be used to create a channel + * @param properties The property string to be used to define the channel + * @param state_timeout The time to wait until state is retrieved in milliseconds. A value of 0 means wait forever. + */ + public DistributedQueue(String groupname, ChannelFactory factory, String properties, long state_timeout) + throws ChannelException + { + if (logger.isDebugEnabled()) + { + logger.debug("DistributedQueue(" + groupname + ',' + properties + ',' + state_timeout); + } + + this.groupname = groupname; + initSignatures(); + internalQueue = new LinkedList(); + channel = (factory != null) ? factory.createChannel((Object)properties) : new JChannel(properties); + disp = new RpcDispatcher(channel, this, this, this); + disp.setDeadlockDetection(false); // To ensure strict FIFO MethodCall + channel.connect(groupname); + start(state_timeout); + } + + public DistributedQueue(JChannel channel) + { + this.groupname = channel.getClusterName(); + this.channel = channel; + init(); + } + + /** + * Uses a user-provided PullPushAdapter to create the dispatcher rather than a Channel. If id is non-null, it will be + * used to register under that id. This is typically used when another building block is already using + * PullPushAdapter, and we want to add this building block in addition. The id is the used to discriminate + * between messages for the various blocks on top of PullPushAdapter. If null, we will assume we are the + * first block created on PullPushAdapter. + * The caller needs to call start(), before using the this block. It gives the opportunity for the caller + * to register as a lessoner for Notifications events. + * @param adapter The PullPushAdapter which to use as underlying transport + * @param id A serializable object (e.g. an Integer) used to discriminate (multiplex/demultiplex) between + * requests/responses for different building blocks on top of PullPushAdapter. + */ + public DistributedQueue(PullPushAdapter adapter, Serializable id) + { + this.channel = (Channel)adapter.getTransport(); + this.groupname = this.channel.getClusterName(); + + initSignatures(); + internalQueue = new LinkedList(); + + disp = new RpcDispatcher(adapter, id, this, this, this); + disp.setDeadlockDetection(false); // To ensure strict FIFO MethodCall + } + + protected final void init() + { + initSignatures(); + internalQueue = new LinkedList(); + disp = new RpcDispatcher(channel, this, this, this); + disp.setDeadlockDetection(false); // To ensure strict FIFO MethodCall + } + + public final void start(long state_timeout) throws ChannelClosedException, ChannelNotConnectedException + { + boolean rc; + logger.debug("DistributedQueue.initState(" + groupname + "): starting state retrieval"); + + rc = channel.getState(null, state_timeout); + + if (rc) + { + logger.info("DistributedQueue.initState(" + groupname + "): state was retrieved successfully"); + } + else + { + logger.info("DistributedQueue.initState(" + groupname + "): state could not be retrieved (first member)"); + } + } + + public Address getLocalAddress() + { + return (channel != null) ? channel.getLocalAddress() : null; + } + + public Channel getChannel() + { + return channel; + } + + public void addNotifier(Notification n) + { + if (n != null && !notifs.contains(n)) + { + notifs.addElement(n); + } + } + + public void removeNotifier(Notification n) + { + notifs.removeElement(n); + } + + public void stop() + { + /*lock the queue from other threads*/ + synchronized (mutex) + { + internalQueue.clear(); + + if (disp != null) + { + disp.stop(); + disp = null; + } + + if (channel != null) + { + channel.close(); + channel = null; + } + + stopped = true; + } + } + + /** + * Add the speficied element at the bottom of the queue + * @param value + */ + public void add(Object value) + { + try + { + Object retval = null; + + RspList rsp = disp.callRemoteMethods(null, "_add", new Object[]{value}, add_signature, GroupRequest.GET_ALL, 0); + Vector results = rsp.getResults(); + + if (results.size() > 0) + { + retval = results.elementAt(0); + + if (logger.isDebugEnabled()) + { + checkResult(rsp, retval); + } + } + } + catch (Exception e) + { + logger.error("Unable to add value " + value, e); + } + + } + + /** + * Add the speficied element at the top of the queue + * @param value + */ + public void addAtHead(Object value) + { + try + { + disp.callRemoteMethods(null, "_addAtHead", new Object[]{value}, addAtHead_signature, GroupRequest.GET_ALL, 0); + } + catch (Exception e) + { + logger.error("Unable to addAtHead value " + value, e); + } + + } + + /** + * Add the speficied collection to the top of the queue. + * Elements are added in the order that they are returned by the specified + * collection's iterator. + * @param values + */ + public void addAll(Collection values) + { + try + { + disp.callRemoteMethods(null, "_addAll", new Object[]{values}, addAll_signature, GroupRequest.GET_ALL, 0); + } + catch (Exception e) + { + logger.error("Unable to addAll value: " + values, e); + } + + } + + public Vector getContents() + { + Vector result = new Vector(); + + for (Iterator e = internalQueue.iterator(); e.hasNext();) + result.add(e.next()); + + return result; + } + + public int size() + { + return internalQueue.size(); + } + + /** + * returns the first object on the queue, without removing it. + * If the queue is empty this object blocks until the first queue object has + * been added + * @return the first object on the queue + */ + public Object peek() + { + Object retval = null; + + try + { + retval = internalQueue.getFirst(); + } + catch (NoSuchElementException e) + { + } + + return retval; + } + + public void reset() + { + try + { + disp.callRemoteMethods(null, "_reset", null, reset_signature, GroupRequest.GET_ALL, 0); + } + catch (Exception e) + { + logger.error("DistributedQueue.reset(" + groupname + ')', e); + } + } + + protected void checkResult(RspList rsp, Object retval) + { + if (logger.isDebugEnabled()) + { + logger.debug("Value updated from " + groupname + " :" + retval); + } + + Vector results = rsp.getResults(); + + for (int i = 0; i < results.size(); i++) + { + Object data = results.elementAt(i); + + if (!data.equals(retval)) + { + logger.error("Reference value differs from returned value " + retval + " != " + data); + } + } + } + + /** + * Try to return the first objet in the queue.It does not wait for an object. + * @return the first object in the queue or null if none were found. + */ + public Object remove() + { + Object retval = null; + RspList rsp = disp.callRemoteMethods(null, "_remove", null, remove_signature, GroupRequest.GET_ALL, internal_timeout); + Vector results = rsp.getResults(); + + if (results.size() > 0) + { + retval = results.elementAt(0); + + if (logger.isDebugEnabled()) + { + checkResult(rsp, retval); + } + } + + return retval; + } + + /** + * @param timeout The time to wait until an entry is retrieved in milliseconds. A value of 0 means wait forever. + * @return the first object in the queue or null if none were found + */ + public Object remove(long timeout) + { + Object retval = null; + long start = System.currentTimeMillis(); + + if (timeout <= 0) + { + while (!stopped && (retval == null)) + { + RspList rsp = disp.callRemoteMethods(null, "_remove", null, remove_signature, GroupRequest.GET_ALL, internal_timeout); + Vector results = rsp.getResults(); + + if (results.size() > 0) + { + retval = results.elementAt(0); + + if (logger.isDebugEnabled()) + { + checkResult(rsp, retval); + } + } + + if (retval == null) + { + try + { + synchronized (mutex) + { + mutex.wait(); + } + } + catch (InterruptedException e) + { + } + } + } + } + else + { + while (((System.currentTimeMillis() - start) < timeout) && !stopped && (retval == null)) + { + RspList rsp = disp.callRemoteMethods(null, "_remove", null, remove_signature, GroupRequest.GET_ALL, internal_timeout); + Vector results = rsp.getResults(); + + if (results.size() > 0) + { + retval = results.elementAt(0); + + if (logger.isDebugEnabled()) + { + checkResult(rsp, retval); + } + } + + if (retval == null) + { + try + { + long delay = timeout - (System.currentTimeMillis() - start); + + synchronized (mutex) + { + if (delay > 0) + { + mutex.wait(delay); + } + } + } + catch (InterruptedException e) + { + } + } + } + } + + return retval; + } + + public String toString() + { + return internalQueue.toString(); + } + + /*------------------------ Callbacks -----------------------*/ + public void _add(Object value) + { + if (logger.isDebugEnabled()) + { + logger.debug(groupname + '@' + getLocalAddress() + " _add(" + value + ')'); + } + + /*lock the queue from other threads*/ + synchronized (mutex) + { + internalQueue.add(value); + + /*wake up all the threads that are waiting for the lock to be released*/ + mutex.notifyAll(); + } + + for (int i = 0; i < notifs.size(); i++) + ((Notification)notifs.elementAt(i)).entryAdd(value); + } + + public void _addAtHead(Object value) + { + /*lock the queue from other threads*/ + synchronized (mutex) + { + internalQueue.addFirst(value); + + /*wake up all the threads that are waiting for the lock to be released*/ + mutex.notifyAll(); + } + + for (int i = 0; i < notifs.size(); i++) + ((Notification)notifs.elementAt(i)).entryAdd(value); + } + + public void _reset() + { + if (logger.isDebugEnabled()) + { + logger.debug(groupname + '@' + getLocalAddress() + " _reset()"); + } + + _private_reset(); + + for (int i = 0; i < notifs.size(); i++) + ((Notification)notifs.elementAt(i)).contentsCleared(); + } + + protected void _private_reset() + { + /*lock the queue from other threads*/ + synchronized (mutex) + { + internalQueue.clear(); + + /*wake up all the threads that are waiting for the lock to be released*/ + mutex.notifyAll(); + } + } + + public Object _remove() + { + Object retval = null; + + try + { + /*lock the queue from other threads*/ + synchronized (mutex) + { + retval = internalQueue.removeFirst(); + + /*wake up all the threads that are waiting for the lock to be released*/ + mutex.notifyAll(); + } + + if (logger.isDebugEnabled()) + { + logger.debug(groupname + '@' + getLocalAddress() + "_remove(" + retval + ')'); + } + + for (int i = 0; i < notifs.size(); i++) + ((Notification)notifs.elementAt(i)).entryRemoved(retval); + } + catch (NoSuchElementException e) + { + logger.debug(groupname + '@' + getLocalAddress() + "_remove(): nothing to remove"); + } + + return retval; + } + + public void _addAll(Collection c) + { + if (logger.isDebugEnabled()) + { + logger.debug(groupname + '@' + getLocalAddress() + " _addAll(" + c + ')'); + } + + /*lock the queue from other threads*/ + synchronized (mutex) + { + internalQueue.addAll(c); + + /*wake up all the threads that are waiting for the lock to be released*/ + mutex.notifyAll(); + } + + for (int i = 0; i < notifs.size(); i++) + ((Notification)notifs.elementAt(i)).contentsSet(c); + } + + /*----------------------------------------------------------*/ + /*-------------------- State Exchange ----------------------*/ + public void receive(Message msg) + { + } + + public byte[] getState() + { + Vector copy = (Vector)getContents().clone(); + + try + { + return Util.objectToByteBuffer(copy); + } + catch (Throwable ex) + { + logger.error("DistributedQueue.getState(): exception marshalling state.", ex); + + return null; + } + } + + public void setState(byte[] new_state) + { + Vector new_copy; + + try + { + new_copy = (Vector)Util.objectFromByteBuffer(new_state); + + if (new_copy == null) + { + return; + } + } + catch (Throwable ex) + { + logger.error("DistributedQueue.setState(): exception unmarshalling state.", ex); + + return; + } + + _private_reset(); // remove all elements + _addAll(new_copy); + } + + /*------------------- Membership Changes ----------------------*/ + public void viewAccepted(View new_view) + { + Vector new_mbrs = new_view.getMembers(); + + if (new_mbrs != null) + { + sendViewChangeNotifications(new_mbrs, members); // notifies observers (joined, left) + members.removeAllElements(); + + for (int i = 0; i < new_mbrs.size(); i++) + members.addElement(new_mbrs.elementAt(i)); + } + } + + /** Called when a member is suspected */ + public void suspect(Address suspected_mbr) + { + ; + } + + /** Block sending and receiving of messages until ViewAccepted is called */ + public void block() + { + } + + void sendViewChangeNotifications(Vector new_mbrs, Vector old_mbrs) + { + Vector joined; + Vector left; + Object mbr; + Notification n; + + if ((notifs.size() == 0) || (old_mbrs == null) || (new_mbrs == null) || (old_mbrs.size() == 0) || + (new_mbrs.size() == 0)) + { + return; + } + + // 1. Compute set of members that joined: all that are in new_mbrs, but not in old_mbrs + joined = new Vector(); + + for (int i = 0; i < new_mbrs.size(); i++) + { + mbr = new_mbrs.elementAt(i); + + if (!old_mbrs.contains(mbr)) + { + joined.addElement(mbr); + } + } + + // 2. Compute set of members that left: all that were in old_mbrs, but not in new_mbrs + left = new Vector(); + + for (int i = 0; i < old_mbrs.size(); i++) + { + mbr = old_mbrs.elementAt(i); + + if (!new_mbrs.contains(mbr)) + { + left.addElement(mbr); + } + } + + for (int i = 0; i < notifs.size(); i++) + { + n = (Notification)notifs.elementAt(i); + n.viewChange(joined, left); + } + } + + final void initSignatures() + { + try + { + if (add_signature == null) + { + add_signature = new Class[] { Object.class }; + } + + if (addAtHead_signature == null) + { + addAtHead_signature = new Class[] { Object.class }; + } + + if (addAll_signature == null) + { + addAll_signature = new Class[] { Collection.class }; + } + + if (reset_signature == null) + { + reset_signature = new Class[0]; + } + + if (remove_signature == null) + { + remove_signature = new Class[0]; + } + } + catch (Throwable ex) + { + logger.error("DistributedQueue.initMethods()", ex); + } + } + + public static void main(String[] args) + { + try + { + // The setup here is kind of weird: + // 1. Create a channel + // 2. Create a DistributedQueue (on the channel) + // 3. Connect the channel (so the HT gets a VIEW_CHANGE) + // 4. Start the HT + // + // A simpler setup is + // DistributedQueue ht = new DistributedQueue("demo", null, + // "file://c:/JGroups-2.0/conf/total-token.xml", 5000); + JChannel c = new JChannel("file:/c:/JGroups-2.0/conf/conf/total-token.xml"); + + DistributedQueue ht = new DistributedQueue(c); + c.connect("demo"); + ht.start(5000); + + ht.add("name"); + ht.add("Michelle Ban"); + + Object old_key = ht.remove(); + System.out.println("old key was " + old_key); + old_key = ht.remove(); + System.out.println("old value was " + old_key); + + ht.add("name 'Michelle Ban'"); + + System.out.println("queue is " + ht); + } + catch (Throwable t) + { + t.printStackTrace(); + } + } +} Index: 3rdParty_sources/jgroups/org/jgroups/blocks/DistributedTree.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/blocks/DistributedTree.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/blocks/DistributedTree.java 17 Aug 2012 14:51:18 -0000 1.1 @@ -0,0 +1,747 @@ +// $Id: DistributedTree.java,v 1.1 2012/08/17 14:51:18 marcin Exp $ + +package org.jgroups.blocks; + + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.*; +import org.jgroups.util.Util; + +import java.io.Serializable; +import java.util.StringTokenizer; +import java.util.Vector; + + + + +/** + * A tree-like structure that is replicated across several members. Updates will be multicast to all group + * members reliably and in the same order. + * @author Bela Ban + * @author Alfonso Olias-Sanz + */ +public class DistributedTree implements MessageListener, MembershipListener { + private Node root=null; + final Vector listeners=new Vector(); + final Vector view_listeners=new Vector(); + final Vector members=new Vector(); + protected Channel channel=null; + protected RpcDispatcher disp=null; + // rc is global and protected so that extensions can detect when + // state has been transferred + protected boolean rc = false; + String groupname="DistributedTreeGroup"; + String channel_properties="udp.xml"; + static final long state_timeout=5000; // wait 5 secs max to obtain state + + /** Determines when the updates have to be sent across the network, avoids sending unnecessary + * messages when there are no member in the group */ + + // Make this protected so that extensions + // can control whether or not to send + protected boolean send_message = false; + + protected static final Log log=LogFactory.getLog(DistributedTree.class); + + + + public interface DistributedTreeListener { + void nodeAdded(String fqn, Serializable element); + + void nodeRemoved(String fqn); + + void nodeModified(String fqn, Serializable old_element, Serializable new_element); + } + + + public interface ViewListener { + void viewChange(Vector new_mbrs, Vector old_mbrs); + } + + + public DistributedTree() { + } + + + public DistributedTree(String groupname, String channel_properties) { + this.groupname=groupname; + if(channel_properties != null) + this.channel_properties=channel_properties; + } + + /* + * Uses a user-provided PullPushAdapter to create the dispatcher rather than a Channel. If id is non-null, it will be + * used to register under that id. This is typically used when another building block is already using + * PullPushAdapter, and we want to add this building block in addition. The id is the used to discriminate + * between messages for the various blocks on top of PullPushAdapter. If null, we will assume we are the + * first block created on PullPushAdapter. + * @param adapter The PullPushAdapter which to use as underlying transport + * @param id A serializable object (e.g. an Integer) used to discriminate (multiplex/demultiplex) between + * requests/responses for different building blocks on top of PullPushAdapter. + * @param state_timeout Max number of milliseconds to wait until state is + * retrieved + */ + public DistributedTree(PullPushAdapter adapter, Serializable id, long state_timeout) + throws ChannelException { + channel = (Channel)adapter.getTransport(); + disp=new RpcDispatcher(adapter, id, this, this, this); + boolean flag = channel.getState(null, state_timeout); + if(flag) { + if(log.isInfoEnabled()) log.info("state was retrieved successfully"); + } + else + if(log.isInfoEnabled()) log.info("state could not be retrieved (must be first member in group)"); + } + + public Object getLocalAddress() { + return channel != null? channel.getLocalAddress() : null; + } + + public void setDeadlockDetection(boolean flag) { + if(disp != null) + disp.setDeadlockDetection(flag); + } + + public void start() throws Exception { + start(8000); + } + + + public void start(long timeout) throws Exception { + if(channel != null) // already started + return; + channel=new JChannel(channel_properties); + disp=new RpcDispatcher(channel, this, this, this); + channel.connect(groupname); + rc=channel.getState(null, timeout); + if(rc) { + if(log.isInfoEnabled()) log.info("state was retrieved successfully"); + } + else + if(log.isInfoEnabled()) log.info("state could not be retrieved (must be first member in group)"); + } + + + public void stop() { + if(channel != null) { + channel.close(); + disp.stop(); + } + channel=null; + disp=null; + } + + + public void addDistributedTreeListener(DistributedTreeListener listener) { + if(!listeners.contains(listener)) + listeners.addElement(listener); + } + + + public void removeDistributedTreeListener(DistributedTreeListener listener) { + listeners.removeElement(listener); + } + + + public void addViewListener(ViewListener listener) { + if(!view_listeners.contains(listener)) + view_listeners.addElement(listener); + } + + + public void removeViewListener(ViewListener listener) { + view_listeners.removeElement(listener); + } + + + public void add(String fqn) { + //Changes done by + //if true, propagate action to the group + if(send_message == true) { + try { + MethodCall call = new MethodCall("_add", new Object[] {fqn}, new String[] {String.class.getName()}); + disp.callRemoteMethods(null, call, GroupRequest.GET_ALL, 0); + } + catch(Exception ex) { + if(log.isErrorEnabled()) log.error("exception=" + ex); + } + } + else { + _add(fqn); + } + } + + public void add(String fqn, Serializable element) { + add(fqn, element, 0); + } + + /** resets an existing node, useful after a merge when you want to tell other + * members of your state, but do not wish to remove and then add as two separate calls */ + public void reset(String fqn, Serializable element) + { + reset(fqn, element, 0); + } + + public void remove(String fqn) { + remove(fqn, 0); + } + + public void add(String fqn, Serializable element, int timeout) { + //Changes done by + //if true, propagate action to the group + if(send_message == true) { + try { + MethodCall call = new MethodCall("_add", new Object[] {fqn, element}, + new String[] {String.class.getName(), Serializable.class.getName()}); + disp.callRemoteMethods(null, call, GroupRequest.GET_ALL, timeout); + } + catch(Exception ex) { + if(log.isErrorEnabled()) log.error("exception=" + ex); + } + } + else { + _add(fqn, element); + } + } + + /** resets an existing node, useful after a merge when you want to tell other + * members of your state, but do not wish to remove and then add as two separate calls */ + public void reset(String fqn, Serializable element, int timeout) + { + //Changes done by + //if true, propagate action to the group + if(send_message == true) { + try { + MethodCall call = new MethodCall("_reset", new Object[] {fqn, element}, + new String[] {String.class.getName(), Serializable.class.getName()}); + disp.callRemoteMethods(null, call, GroupRequest.GET_ALL, timeout); + } + catch(Exception ex) { + if(log.isErrorEnabled()) log.error("exception=" + ex); + } + } + else { + _add(fqn, element); + } + } + + public void remove(String fqn, int timeout) { + //Changes done by + //if true, propagate action to the group + if(send_message == true) { + try { + MethodCall call = new MethodCall("_remove", new Object[] {fqn}, new String[] {String.class.getName()}); + disp.callRemoteMethods(null, call, GroupRequest.GET_ALL, timeout); + } + catch(Exception ex) { + if(log.isErrorEnabled()) log.error("exception=" + ex); + } + } + else { + _remove(fqn); + } + } + + + public boolean exists(String fqn) { + return fqn != null && (findNode(fqn) != null); + } + + + public Serializable get(String fqn) { + Node n=null; + + if(fqn == null) return null; + n=findNode(fqn); + if(n != null) { + return n.element; + } + return null; + } + + + public void set(String fqn, Serializable element) { + set(fqn, element, 0); + } + + public void set(String fqn, Serializable element, int timeout) { + //Changes done by + //if true, propagate action to the group + if(send_message == true) { + try { + MethodCall call = new MethodCall("_set", new Object[] {fqn, element}, + new String[] {String.class.getName(), Serializable.class.getName()}); + disp.callRemoteMethods(null, call, GroupRequest.GET_ALL, timeout); + } + catch(Exception ex) { + if(log.isErrorEnabled()) log.error("exception=" + ex); + } + } + else { + _set(fqn, element); + } + } + + + /** Returns all children of a Node as strings */ + public Vector getChildrenNames(String fqn) { + Vector ret=new Vector(); + Node n; + + if(fqn == null) return ret; + n=findNode(fqn); + if(n == null || n.children == null) return ret; + for(int i=0; i < n.children.size(); i++) + ret.addElement(((Node)n.children.elementAt(i)).name); + return ret; + } + + + public String print() { + StringBuilder sb=new StringBuilder(); + int indent=0; + + if(root == null) + return "/"; + + sb.append(root.print(indent)); + return sb.toString(); + } + + + /** Returns all children of a Node as Nodes */ + Vector getChildren(String fqn) { + Node n; + + if(fqn == null) return null; + n=findNode(fqn); + if(n == null) return null; + return n.children; + } + + /** + * Returns the name of the group that the DistributedTree is connected to + * @return String + */ + public String getGroupName() {return groupname;} + + /** + * Returns the Channel the DistributedTree is connected to + * @return Channel + */ + public Channel getChannel() {return channel;} + + /** + * Returns the number of current members joined to the group + * @return int + */ + public int getGroupMembersNumber() {return members.size();} + + + + + /*--------------------- Callbacks --------------------------*/ + + public void _add(String fqn) { + _add(fqn, null); + } + + + public void _add(String fqn, Serializable element) { + Node curr, n; + StringTokenizer tok; + String child_name; + String tmp_fqn=""; + + if(root == null) { + root=new Node("/", null); + notifyNodeAdded("/", null); + } + if(fqn == null) + return; + curr=root; + tok=new StringTokenizer(fqn, "/"); + + while(tok.hasMoreTokens()) { + child_name=tok.nextToken(); + tmp_fqn=tmp_fqn + '/' + child_name; + n=curr.findChild(child_name); + if(n == null) { + n=new Node(child_name, null); + curr.addChild(n); + if(!tok.hasMoreTokens()) { + n.element=element; + notifyNodeAdded(tmp_fqn, element); + return; + } + else + notifyNodeAdded(tmp_fqn, null); + } + curr=n; + } + // If the element is not null, we install it and notify the + // listener app that the node is modified. + if(element != null){ + curr.element=element; + notifyNodeModified(fqn, null, element); + } + } + + + public void _remove(String fqn) { + Node curr, n; + StringTokenizer tok; + String child_name=null; + + if(fqn == null || root == null) + return; + curr=root; + tok=new StringTokenizer(fqn, "/"); + + while(tok.countTokens() > 1) { + child_name=tok.nextToken(); + n=curr.findChild(child_name); + if(n == null) // node does not exist + return; + curr=n; + } + try { + child_name=tok.nextToken(); + if(child_name != null) { + n=curr.removeChild(child_name); + if(n != null) + notifyNodeRemoved(fqn); + } + } + catch(Exception ex) { + } + } + + + public void _set(String fqn, Serializable element) { + Node n; + Serializable old_el=null; + + if(fqn == null || element == null) return; + n=findNode(fqn); + if(n == null) { + if(log.isErrorEnabled()) log.error("node " + fqn + " not found"); + return; + } + old_el=n.element; + n.element=element; + notifyNodeModified(fqn, old_el, element); + } + + /** similar to set, but does not error if node does not exist, but rather does an add instead */ + public void _reset(String fqn, Serializable element) { + Node n; + Serializable old_el=null; + + if(fqn == null || element == null) return; + n=findNode(fqn); + if(n == null) { + _add(fqn, element); + } + else { + old_el=n.element; + n.element=element; + } + notifyNodeModified(fqn, old_el, element); + } + + /*----------------- End of Callbacks ----------------------*/ + + + + + + + /*-------------------- State Exchange ----------------------*/ + + public void receive(Message msg) { + } + + /** Return a copy of the tree */ + public byte[] getState() { + Object copy=root != null? root.copy() : null; + try { + return Util.objectToByteBuffer(copy); + } + catch(Throwable ex) { + if(log.isErrorEnabled()) log.error("exception marshalling state: " + ex); + return null; + } + } + + public void setState(byte[] data) { + Object new_state; + + try { + new_state=Util.objectFromByteBuffer(data); + } + catch(Throwable ex) { + if(log.isErrorEnabled()) log.error("exception unmarshalling state: " + ex); + return; + } + if(new_state == null) return; + if(!(new_state instanceof Node)) { + if(log.isErrorEnabled()) log.error("object is not of type 'Node'"); + return; + } + root=((Node)new_state).copy(); + + // State transfer needs to notify listeners in the new + // cluster member about everything that exists. This + // is working ok now. + this.notifyAllNodesCreated(root, ""); + } + + + + /*------------------- Membership Changes ----------------------*/ + + public void viewAccepted(View new_view) { + Vector new_mbrs=new_view.getMembers(); + + if(new_mbrs != null) { + sendViewChangeNotifications(new_mbrs, members); // notifies observers (joined, left) + members.removeAllElements(); + for(int i=0; i < new_mbrs.size(); i++) + members.addElement(new_mbrs.elementAt(i)); + } + //if size is bigger than one, there are more peers in the group + //otherwise there is only one server. + send_message=true; + send_message=members.size() > 1; + } + + + /** Called when a member is suspected */ + public void suspect(Address suspected_mbr) { + } + + + /** Block sending and receiving of messages until ViewAccepted is called */ + public void block() { + } + + + void sendViewChangeNotifications(Vector new_mbrs, Vector old_mbrs) { + Vector joined, left; + Object mbr; + + if(view_listeners.isEmpty() || old_mbrs == null || new_mbrs == null) + return; + + + // 1. Compute set of members that joined: all that are in new_mbrs, but not in old_mbrs + joined=new Vector(); + for(int i=0; i < new_mbrs.size(); i++) { + mbr=new_mbrs.elementAt(i); + if(!old_mbrs.contains(mbr)) + joined.addElement(mbr); + } + + + // 2. Compute set of members that left: all that were in old_mbrs, but not in new_mbrs + left=new Vector(); + for(int i=0; i < old_mbrs.size(); i++) { + mbr=old_mbrs.elementAt(i); + if(!new_mbrs.contains(mbr)) + left.addElement(mbr); + } + notifyViewChange(joined, left); + } + + + private Node findNode(String fqn) { + Node curr=root; + StringTokenizer tok; + String child_name; + + if(fqn == null || root == null) return null; + if("/".equals(fqn) || "".equals(fqn)) + return root; + + tok=new StringTokenizer(fqn, "/"); + while(tok.hasMoreTokens()) { + child_name=tok.nextToken(); + curr=curr.findChild(child_name); + if(curr == null) return null; + } + return curr; + } + + + void notifyNodeAdded(String fqn, Serializable element) { + for(int i=0; i < listeners.size(); i++) + ((DistributedTreeListener)listeners.elementAt(i)).nodeAdded(fqn, element); + } + + void notifyNodeRemoved(String fqn) { + for(int i=0; i < listeners.size(); i++) + ((DistributedTreeListener)listeners.elementAt(i)).nodeRemoved(fqn); + } + + void notifyNodeModified(String fqn, Serializable old_element, Serializable new_element) { + for(int i=0; i < listeners.size(); i++) + ((DistributedTreeListener)listeners.elementAt(i)).nodeModified(fqn, old_element, new_element); + } + + /** Generates NodeAdded notifications for all nodes of the tree. This is called whenever the tree is + initially retrieved (state transfer) */ + void notifyAllNodesCreated(Node curr, String tmp_fqn) { + Node n; + // We need a local string here to handle the empty string (root) + // otherwise, we start off with two slashes in the path. + String path = ""; + if(curr == null) return; + if(curr.name == null) { + if(log.isErrorEnabled()) log.error("curr.name is null"); + return; + } + // If we're the root node, then we supply a "starter" slash. + // This lets us properly initiate the recursion with an empty + // string, and then prepend a slash for each additional depth + path = (curr.equals(root)) ? "/" : tmp_fqn; + + // Recursion must occur _before_ we look for children, or we + // never notifyNodeAdded() for leaf nodes. + notifyNodeAdded(path, curr.element); + if(curr.children != null) { + for(int i=0; i < curr.children.size(); i++) { + n=(Node)curr.children.elementAt(i); + notifyAllNodesCreated(n, tmp_fqn + '/' + n.name); + } + } + } + + + void notifyViewChange(Vector new_mbrs, Vector old_mbrs) { + for(int i=0; i < view_listeners.size(); i++) + ((ViewListener)view_listeners.elementAt(i)).viewChange(new_mbrs, old_mbrs); + } + + + private static class Node implements Serializable { + String name=null; + Vector children=null; + Serializable element=null; + private static final long serialVersionUID=-635336369135391033L; + + + Node() { + } + + Node(String name, Serializable element) { + this.name=name; + this.element=element; + } + + + void addChild(String relative_name, Serializable element) { + if(relative_name == null) + return; + if(children == null) + children=new Vector(); + else { + if(!children.contains(relative_name)) + children.addElement(new Node(relative_name, element)); + } + } + + + void addChild(Node n) { + if(n == null) return; + if(children == null) + children=new Vector(); + if(!children.contains(n)) + children.addElement(n); + } + + + Node removeChild(String rel_name) { + Node n=findChild(rel_name); + + if(n != null) + children.removeElement(n); + return n; + } + + + Node findChild(String relative_name) { + Node child; + + if(children == null || relative_name == null) + return null; + for(int i=0; i < children.size(); i++) { + child=(Node)children.elementAt(i); + if(child.name == null) { + if(log.isErrorEnabled()) log.error("child.name is null for " + relative_name); + continue; + } + + if(child.name.equals(relative_name)) + return child; + } + + return null; + } + + + public boolean equals(Object other) { + return other != null && ((Node)other).name != null && name != null && name.equals(((Node)other).name); + } + + + Node copy() { + Node ret=new Node(name, element); + + if(children != null) + ret.children=(Vector)children.clone(); + return ret; + } + + + String print(int indent) { + StringBuilder sb=new StringBuilder(); + boolean is_root=name != null && "/".equals(name); + + for(int i=0; i < indent; i++) + sb.append(' '); + if(!is_root) { + if(name == null) + sb.append("/"); + else { + sb.append('/' + name); + // if(element != null) sb.append(" --> " + element); + } + } + sb.append('\n'); + if(children != null) { + if(is_root) + indent=0; + else + indent+=4; + for(int i=0; i < children.size(); i++) + sb.append(((Node)children.elementAt(i)).print(indent)); + } + return sb.toString(); + } + + + public String toString() { + if(element != null) + return "[name: " + name + ", element: " + element + ']'; + else + return "[name: " + name + ']'; + } + + } + + +} + + Index: 3rdParty_sources/jgroups/org/jgroups/blocks/GroupRequest.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/blocks/GroupRequest.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/blocks/GroupRequest.java 17 Aug 2012 14:51:18 -0000 1.1 @@ -0,0 +1,678 @@ +package org.jgroups.blocks; + + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.Address; +import org.jgroups.Message; +import org.jgroups.Transport; +import org.jgroups.View; +import org.jgroups.annotations.GuardedBy; +import org.jgroups.util.Command; +import org.jgroups.util.Rsp; +import org.jgroups.util.RspList; + +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + + +/** + * Sends a message to all members of the group and waits for all responses (or timeout). Returns a + * boolean value (success or failure). Results (if any) can be retrieved when done.

+ * The supported transport to send requests is currently either a RequestCorrelator or a generic + * Transport. One of them has to be given in the constructor. It will then be used to send a + * request. When a message is received by either one, the receiveResponse() of this class has to + * be called (this class does not actively receive requests/responses itself). Also, when a view change + * or suspicion is received, the methods viewChange() or suspect() of this class have to be called.

+ * When started, an array of responses, correlating to the membership, is created. Each response + * is added to the corresponding field in the array. When all fields have been set, the algorithm + * terminates. + * This algorithm can optionally use a suspicion service (failure detector) to detect (and + * exclude from the membership) fauly members. If no suspicion service is available, timeouts + * can be used instead (see execute()). When done, a list of suspected members + * can be retrieved.

+ * Because a channel might deliver requests, and responses to different requests, the + * GroupRequest class cannot itself receive and process requests/responses from the + * channel. A mechanism outside this class has to do this; it has to determine what the responses + * are for the message sent by the execute() method and call receiveResponse() + * to do so.

+ * Requirements: lossless delivery, e.g. acknowledgment-based message confirmation. + * @author Bela Ban + * @version $Id: GroupRequest.java,v 1.1 2012/08/17 14:51:18 marcin Exp $ + */ +public class GroupRequest implements RspCollector, Command { + /** return only first response */ + public static final int GET_FIRST=1; + + /** return all responses */ + public static final int GET_ALL=2; + + /** return majority (of all non-faulty members) */ + public static final int GET_MAJORITY=3; + + /** return majority (of all members, may block) */ + public static final int GET_ABS_MAJORITY=4; + + /** return n responses (may block) */ + public static final int GET_N=5; + + /** return no response (async call) */ + public static final int GET_NONE=6; + + private Address caller; + + private final Lock lock=new ReentrantLock(); + + /** Is set as soon as the request has received all required responses */ + private final Condition completed=lock.newCondition(); + + /** Map. Maps requests and responses */ + @GuardedBy("lock") + private final Map requests=new HashMap(); + + + /** bounded queue of suspected members */ + @GuardedBy("lock") + private final List

suspects=new ArrayList
(); + + /** list of members, changed by viewChange() */ + @GuardedBy("lock") + private final Collection
members=new TreeSet
(); + + /** keep suspects vector bounded */ + private static final int max_suspects=40; + final protected Message request_msg; + final protected RequestCorrelator corr; // either use RequestCorrelator or ... + protected Transport transport; // Transport (one of them has to be non-null) + + protected RspFilter rsp_filter=null; + + protected int rsp_mode=GET_ALL; + protected boolean done=false; + protected long timeout=0; + protected int expected_mbrs=0; + + private static final Log log=LogFactory.getLog(GroupRequest.class); + + /** to generate unique request IDs (see getRequestId()) */ + private static long last_req_id=1; + + private long req_id=-1; // request ID for this request + + + + + /** + @param m The message to be sent + @param corr The request correlator to be used. A request correlator sends requests tagged with + a unique ID and notifies the sender when matching responses are received. The + reason GroupRequest uses it instead of a Transport is + that multiple requests/responses might be sent/received concurrently. + @param members The initial membership. This value reflects the membership to which the request + is sent (and from which potential responses are expected). Is reset by reset(). + @param rsp_mode How many responses are expected. Can be +
    +
  1. GET_ALL: wait for all responses from non-suspected members. + A suspicion service might warn + us when a member from which a response is outstanding has crashed, so it can + be excluded from the responses. If no suspision service is available, a + timeout can be used (a value of 0 means wait forever). If a timeout of + 0 is used, no suspicion service is available and a member from which we + expect a response has crashed, this methods blocks forever !. +
  2. GET_FIRST: wait for the first available response. +
  3. GET_MAJORITY: wait for the majority of all responses. The + majority is re-computed when a member is suspected. +
  4. GET_ABS_MAJORITY: wait for the majority of + all members. + This includes failed members, so it may block if no timeout is specified. +
  5. GET_N: wait for N members. + Return if n is >= membership+suspects. +
  6. GET_NONE: don't wait for any response. Essentially send an + asynchronous message to the group members. +
+ */ + public GroupRequest(Message m, RequestCorrelator corr, Vector
members, int rsp_mode) { + request_msg=m; + this.corr=corr; + this.rsp_mode=rsp_mode; + reset(members); + // suspects.removeAllElements(); // bela Aug 23 2002: made suspects bounded + } + + + /** + @param timeout Time to wait for responses (ms). A value of <= 0 means wait indefinitely + (e.g. if a suspicion service is available; timeouts are not needed). + */ + public GroupRequest(Message m, RequestCorrelator corr, Vector
members, int rsp_mode, + long timeout, int expected_mbrs) { + this(m, corr, members, rsp_mode); + if(timeout > 0) + this.timeout=timeout; + this.expected_mbrs=expected_mbrs; + } + + + public GroupRequest(Message m, Transport transport, Vector
members, int rsp_mode) { + corr = null; + request_msg=m; + this.transport=transport; + this.rsp_mode=rsp_mode; + reset(members); + // suspects.removeAllElements(); // bela Aug 23 2002: make suspects bounded + } + + + /** + * @param timeout Time to wait for responses (ms). A value of <= 0 means wait indefinitely + * (e.g. if a suspicion service is available; timeouts are not needed). + */ + public GroupRequest(Message m, Transport transport, Vector
members, + int rsp_mode, long timeout, int expected_mbrs) { + this(m, transport, members, rsp_mode); + if(timeout > 0) + this.timeout=timeout; + this.expected_mbrs=expected_mbrs; + } + + public Address getCaller() { + return caller; + } + + public void setCaller(Address caller) { + this.caller=caller; + } + + public void setResponseFilter(RspFilter filter) { + rsp_filter=filter; + } + + public boolean execute() throws Exception { + return execute(false); + } + + /** + * Sends the message. Returns when n responses have been received, or a + * timeout has occurred. n can be the first response, all + * responses, or a majority of the responses. + */ + public boolean execute(boolean use_anycasting) throws Exception { + if(corr == null && transport == null) { + if(log.isErrorEnabled()) log.error("both corr and transport are null, cannot send group request"); + return false; + } + + Vector
targets=null; + lock.lock(); + try { + targets=new Vector
(members); + req_id=getRequestId(); + // reset(null); // clear 'responses' array + + for(Address suspect: suspects) { // mark all suspects in 'received' array + Rsp rsp=requests.get(suspect); + if(rsp != null) { + rsp.setSuspected(true); + break; // we can break here because we ensure there are no duplicate members + } + } + } + finally { + lock.unlock(); + } + + sendRequest(targets, req_id, use_anycasting); + + lock.lock(); + try { + done=false; + boolean retval=collectResponses(timeout); + if(retval == false && log.isTraceEnabled()) + log.trace("call did not execute correctly, request is " + this.toString()); + return retval; + } + finally { + done=true; + lock.unlock(); + } + } + + + + /** + * This method sets the membership variable to the value of + * members. It requires that the caller already hold the + * rsp_mutex lock. + * @param mbrs The new list of members + */ + public final void reset(Vector
mbrs) { + lock.lock(); + try { + if(mbrs != null) { + requests.clear(); + for(Address mbr: mbrs) { + requests.put(mbr, new Rsp(mbr)); + } + // maintain local membership + this.members.clear(); + this.members.addAll(mbrs); + } + else { + for(Rsp rsp: requests.values()) { + rsp.setReceived(false); + rsp.setValue(null); + } + } + } + finally { + lock.unlock(); + } + } + + + /* ---------------------- Interface RspCollector -------------------------- */ + /** + * Callback (called by RequestCorrelator or Transport). + * Adds a response to the response table. When all responses have been received, + * execute() returns. + */ + public void receiveResponse(Object response_value, Address sender) { + lock.lock(); + try { + if(done) { + if(log.isTraceEnabled()) log.trace("command is done; cannot add response !"); + return; + } + if(suspects.contains(sender)) { + if(log.isWarnEnabled()) log.warn("received response from suspected member " + sender + "; discarding"); + return; + } + + if(rsp_filter != null && !rsp_filter.isAcceptable(response_value, sender)) { + if(!rsp_filter.needMoreResponses()) { + done=true; + completed.signalAll(); // we're done as we don't need more responses + } + return; + } + + + Rsp rsp=requests.get(sender); + if(rsp != null) { + if(rsp.wasReceived() == false) { + rsp.setValue(response_value); + rsp.setReceived(true); + if(log.isTraceEnabled()) + log.trace(new StringBuilder("received response for request ").append(req_id).append(", sender="). + append(sender).append(", val=").append(response_value)); + if(rsp_filter != null && !rsp_filter.needMoreResponses()) + done=true; + completed.signalAll(); // wakes up execute() + } + } + } + finally { + lock.unlock(); + } + } + + + /** + * Callback (called by RequestCorrelator or Transport). + * Report to GroupRequest that a member is reported as faulty (suspected). + * This method would probably be called when getting a suspect message from a failure detector + * (where available). It is used to exclude faulty members from the response list. + */ + public void suspect(Address suspected_member) { + if(suspected_member == null) + return; + + lock.lock(); + try { + addSuspect(suspected_member); + Rsp rsp=requests.get(suspected_member); + if(rsp != null) { + rsp.setSuspected(true); + rsp.setValue(null); + completed.signalAll(); + } + } + finally { + lock.unlock(); + } + } + + + /** + * Any member of 'membership' that is not in the new view is flagged as + * SUSPECTED. Any member in the new view that is not in the + * membership (ie, the set of responses expected for the current RPC) will + * not be added to it. If we did this we might run into the + * following problem: + *
    + *
  • Membership is {A,B} + *
  • A sends a synchronous group RPC (which sleeps for 60 secs in the + * invocation handler) + *
  • C joins while A waits for responses from A and B + *
  • If this would generate a new view {A,B,C} and if this expanded the + * response set to {A,B,C}, A would wait forever on C's response because C + * never received the request in the first place, therefore won't send a + * response. + *
+ */ + public void viewChange(View new_view) { + Address mbr; + Vector
mbrs=new_view != null? new_view.getMembers() : null; + + lock.lock(); + try { + if(requests == null || requests.isEmpty() || mbrs == null) + return; + + this.members.clear(); + this.members.addAll(mbrs); + + Rsp rsp; + Set
tmp=null; + for(Map.Entry entry: requests.entrySet()) { + mbr=entry.getKey(); + if(!mbrs.contains(mbr)) { + if(tmp == null) + tmp=new HashSet
(); + tmp.add(mbr); + addSuspect(mbr); + rsp=entry.getValue(); + rsp.setValue(null); + rsp.setSuspected(true); + } + } + + if(tmp != null) { + for(Address suspect: tmp) { + addSuspect(suspect); + } + completed.signalAll(); + } + } + finally { + lock.unlock(); + } + } + + + /* -------------------- End of Interface RspCollector ----------------------------------- */ + + + + /** Returns the results as a RspList */ + public RspList getResults() { + lock.lock(); + try { + Collection rsps=requests.values(); + return new RspList(rsps); + } + finally { + lock.unlock(); + } + } + + + public String toString() { + StringBuilder ret=new StringBuilder(128); + ret.append("[req_id=").append(req_id).append('\n'); + if(caller != null) + ret.append("caller=").append(caller).append("\n"); + + Address mbr; + Rsp rsp; + lock.lock(); + try { + if(!requests.isEmpty()) { + ret.append("entries:\n"); + for(Map.Entry entry: requests.entrySet()) { + mbr=entry.getKey(); + rsp=entry.getValue(); + ret.append(mbr).append(": ").append(rsp).append("\n"); + } + } + } + finally { + lock.unlock(); + } + return ret.toString(); + } + + + public int getNumSuspects() { + return suspects.size(); + } + + /** Returns the list of suspected members. An attempt to modify the return value will throw an excxeption */ + public Vector
getSuspects() { + return new Vector
(suspects); + } + + + public boolean isDone() { + return done; + } + + + + /* --------------------------------- Private Methods -------------------------------------*/ + + private static int determineMajority(int i) { + return i < 2? i : (i / 2) + 1; + } + + /** Generates a new unique request ID */ + private static synchronized long getRequestId() { + long result=System.currentTimeMillis(); + if(result <= last_req_id) { + result=last_req_id + 1; + } + last_req_id=result; + return result; + } + + /** This method runs with lock locked (called by execute()). */ + @GuardedBy("lock") + private boolean collectResponses(long timeout) throws Exception { + if(timeout <= 0) { + while(true) { /* Wait for responses: */ + adjustMembership(); // may not be necessary, just to make sure... + if(responsesComplete()) { + if(corr != null) { + corr.done(req_id); + } + if(log.isTraceEnabled() && rsp_mode != GET_NONE) { + log.trace("received all responses: " + toString()); + } + return true; + } + try { + completed.await(); + } + catch(Exception e) { + } + } + } + else { + long start_time=System.currentTimeMillis(); + long timeout_time=start_time + timeout; + while(timeout > 0) { /* Wait for responses: */ + if(responsesComplete()) { + if(corr != null) + corr.done(req_id); + if(log.isTraceEnabled() && rsp_mode != GET_NONE) { + log.trace("received all responses: " + toString()); + } + return true; + } + timeout=timeout_time - System.currentTimeMillis(); + if(timeout > 0) { + try { + completed.await(timeout, TimeUnit.MILLISECONDS); + } + catch(Exception e) { + } + } + } + if(corr != null) { + corr.done(req_id); + } + if(log.isTraceEnabled()) + log.trace("timed out waiting for responses"); + + return false; + } + } + + + private void sendRequest(Vector
targetMembers, long requestId,boolean use_anycasting) throws Exception { + try { + if(log.isTraceEnabled()) log.trace(new StringBuilder("sending request (id=").append(req_id).append(')')); + if(corr != null) { + corr.sendRequest(requestId, targetMembers, request_msg, rsp_mode == GET_NONE? null : this, use_anycasting); + } + else { + if(use_anycasting) { + for(Address mbr: targetMembers) { + Message copy=request_msg.copy(true); + copy.setDest(mbr); + transport.send(copy); + } + } + else { + transport.send(request_msg); + } + } + } + catch(Exception ex) { + if(corr != null) + corr.done(requestId); + throw ex; + } + } + + + @GuardedBy("lock") + private boolean responsesComplete() { + int num_received=0, num_not_received=0, num_suspected=0; + final int num_total=requests.size(); + + if(done) + return true; + + for(Rsp rsp: requests.values()) { + if(rsp.wasReceived()) { + num_received++; + } + else { + if(rsp.wasSuspected()) { + num_suspected++; + } + else { + num_not_received++; + } + } + } + + switch(rsp_mode) { + case GET_FIRST: + if(num_received > 0) + return true; + if(num_suspected >= num_total) + // e.g. 2 members, and both suspected + return true; + break; + case GET_ALL: + return num_received + num_suspected >= num_total; + case GET_MAJORITY: + int majority=determineMajority(num_total); + if(num_received + num_suspected >= majority) + return true; + break; + case GET_ABS_MAJORITY: + majority=determineMajority(num_total); + if(num_received >= majority) + return true; + break; + case GET_N: + if(expected_mbrs >= num_total) { + rsp_mode=GET_ALL; + return responsesComplete(); + } + return num_received >= expected_mbrs || num_received + num_not_received < expected_mbrs && num_received + num_suspected >= expected_mbrs; + case GET_NONE: + return true; + default : + if(log.isErrorEnabled()) log.error("rsp_mode " + rsp_mode + " unknown !"); + break; + } + return false; + } + + + + + + /** + * Adjusts the 'received' array in the following way: + *
    + *
  • if a member P in 'membership' is not in 'members', P's entry in the 'received' array + * will be marked as SUSPECTED + *
  • if P is 'suspected_mbr', then P's entry in the 'received' array will be marked + * as SUSPECTED + *
+ * This call requires exclusive access to rsp_mutex (called by getResponses() which has + * a the rsp_mutex locked, so this should not be a problem). This method needs to have lock held. + */ + @GuardedBy("lock") + private void adjustMembership() { + if(requests.isEmpty()) + return; + + Address mbr; + Rsp rsp; + for(Map.Entry entry: requests.entrySet()) { + mbr=entry.getKey(); + if((!this.members.contains(mbr)) || suspects.contains(mbr)) { + addSuspect(mbr); + rsp=entry.getValue(); + rsp.setValue(null); + rsp.setSuspected(true); + } + } + } + + /** + * Adds a member to the 'suspects' list. Removes oldest elements from 'suspects' list + * to keep the list bounded ('max_suspects' number of elements), Requires lock to be held + */ + @GuardedBy("lock") + private void addSuspect(Address suspected_mbr) { + if(!suspects.contains(suspected_mbr)) { + suspects.add(suspected_mbr); + while(suspects.size() >= max_suspects && !suspects.isEmpty()) + suspects.remove(0); // keeps queue bounded + } + } + + private static String modeToString(int m) { + switch(m) { + case GET_FIRST: return "GET_FIRST"; + case GET_ALL: return "GET_ALL"; + case GET_MAJORITY: return "GET_MAJORITY"; + case GET_ABS_MAJORITY: return "GET_ABS_MAJORITY"; + case GET_N: return "GET_N"; + case GET_NONE: return "GET_NONE"; + default: return " (" + m + ")"; + } + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/blocks/LockManager.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/blocks/LockManager.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/blocks/LockManager.java 17 Aug 2012 14:51:17 -0000 1.1 @@ -0,0 +1,87 @@ +package org.jgroups.blocks; + +import org.jgroups.ChannelException; + +/** + * LockManager represents generic lock manager that allows + * obtaining and releasing locks on objects. + * + * @author Roman Rokytskyy (rrokytskyy@acm.org) + * @author Robert Schaffar-Taurok (robert@fusion.at) + * @version $Id: LockManager.java,v 1.1 2012/08/17 14:51:17 marcin Exp $ + */ +public interface LockManager { + + /** + * Obtain lock on obj for specified owner. + * Implementation should try to obtain lock few times within the + * specified timeout. + * + * @param obj obj to lock, usually not full object but object's ID. + * @param owner object identifying entity that will own the lock. + * @param timeout maximum time that we grant to obtain a lock. + * + * @throws LockNotGrantedException if lock is not granted within + * specified period. + * + * @throws ClassCastException if obj and/or + * owner is not of type that implementation expects to get + * (for example, when distributed lock manager obtains non-serializable + * obj or owner). + * + * @throws ChannelException if something bad happened to communication + * channel. + */ + void lock(Object obj, Object owner, int timeout) + throws LockNotGrantedException, ClassCastException, ChannelException; + + /** + * Release lock on obj owned by specified owner. + * + * since 2.2.9 this method is only a wrapper for + * unlock(Object lockId, Object owner, boolean releaseMultiLocked). + * Use that with releaseMultiLocked set to true if you want to be able to + * release multiple locked locks (for example after a merge) + * + * @param obj obj to lock, usually not full object but object's ID. + * @param owner object identifying entity that will own the lock. + * + * @throws LockOwnerMismatchException if lock is owned by another object. + * + * @throws ClassCastException if obj and/or + * owner is not of type that implementation expects to get + * (for example, when distributed lock manager obtains non-serializable + * obj or owner). + * + * @throws ChannelException if something bad happened to communication + * channel. + */ + void unlock(Object obj, Object owner) + throws LockNotReleasedException, ClassCastException, ChannelException; + + /** + * Release lock on obj owned by specified owner. + * + * @param obj obj to lock, usually not full object but object's ID. + * @param owner object identifying entity that will own the lock. + * @param releaseMultiLocked force unlocking of the lock if the local + * lockManager owns the lock even if another lockManager owns the same lock + * + * @throws LockOwnerMismatchException if lock is owned by another object. + * + * @throws ClassCastException if obj and/or + * owner is not of type that implementation expects to get + * (for example, when distributed lock manager obtains non-serializable + * obj or owner). + * + * @throws ChannelException if something bad happened to communication + * channel. + * + * @throws LockMultiLockedException if the lock was unlocked, but another + * node already held the lock + */ + void unlock(Object obj, Object owner, boolean releaseMultiLocked) + throws LockNotReleasedException, ClassCastException, ChannelException, LockMultiLockedException; + + +} \ No newline at end of file Index: 3rdParty_sources/jgroups/org/jgroups/blocks/LockMultiLockedException.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/blocks/LockMultiLockedException.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/blocks/LockMultiLockedException.java 17 Aug 2012 14:51:17 -0000 1.1 @@ -0,0 +1,23 @@ +package org.jgroups.blocks; + + +/** + * Thrown by the {@link org.jgroups.blocks.DistributedLockManager#unlock(Object, Object, boolean)} method if a lock is only locally released, because it is locked + * by multiple DistributedLockManagers. This can happen after a merge for example. + * + * @author Robert Schaffar-Taurok (robert@fusion.at) + * @version $Id: LockMultiLockedException.java,v 1.1 2012/08/17 14:51:17 marcin Exp $ + */ +public class LockMultiLockedException extends Exception { + + private static final long serialVersionUID = 3719208228960070835L; + + public LockMultiLockedException() { + super(); + } + + public LockMultiLockedException(String s) { + super(s); + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/blocks/LockNotGrantedException.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/blocks/LockNotGrantedException.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/blocks/LockNotGrantedException.java 17 Aug 2012 14:51:18 -0000 1.1 @@ -0,0 +1,21 @@ +package org.jgroups.blocks; + +/** + * This exception indicated that lock manager refused to give a lock on + * some resource. + * + * @author Roman Rokytskyy (rrokytskyy@acm.org) + */ +public class LockNotGrantedException extends Exception { + + private static final long serialVersionUID = 4074824788210185433L; + + public LockNotGrantedException() { + super(); + } + + public LockNotGrantedException(String s) { + super(s); + } + +} \ No newline at end of file Index: 3rdParty_sources/jgroups/org/jgroups/blocks/LockNotReleasedException.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/blocks/LockNotReleasedException.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/blocks/LockNotReleasedException.java 17 Aug 2012 14:51:18 -0000 1.1 @@ -0,0 +1,21 @@ +package org.jgroups.blocks; + +/** + * This exception indicated that lock manager refused to release a lock on + * some resource. + * + * @author Roman Rokytskyy (rrokytskyy@acm.org) + */ +public class LockNotReleasedException extends Exception { + + private static final long serialVersionUID = -350403929687059570L; + + public LockNotReleasedException() { + super(); + } + + public LockNotReleasedException(String s) { + super(s); + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/blocks/LockingException.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/blocks/LockingException.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/blocks/LockingException.java 17 Aug 2012 14:51:18 -0000 1.1 @@ -0,0 +1,35 @@ +// $Id: LockingException.java,v 1.1 2012/08/17 14:51:18 marcin Exp $ + + +package org.jgroups.blocks; + +import java.util.Map; + + +public class LockingException extends Exception { + + private static final long serialVersionUID = -712594616520011007L; + + Map failed_lockers=null; // list of members who failed acquiring locks (keys=Address, values=exception string) + + public LockingException(String msg) { + super(msg); + } + + public LockingException(Map m) { + super("LockingException"); + failed_lockers=m; + } + + + public String toString() { + StringBuilder sb=new StringBuilder(); + + sb.append(super.toString()); + + if(failed_lockers != null && failed_lockers.size() > 0) + sb.append(" (failed members: ").append(failed_lockers); + return sb.toString(); + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/blocks/MembershipListenerAdapter.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/blocks/MembershipListenerAdapter.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/blocks/MembershipListenerAdapter.java 17 Aug 2012 14:51:19 -0000 1.1 @@ -0,0 +1,92 @@ +package org.jgroups.blocks; + +import org.jgroups.Address; +import org.jgroups.MembershipListener; +import org.jgroups.View; +import org.jgroups.ExtendedMembershipListener; + +import java.util.HashSet; + +/** + * This class provides multiplexing possibilities for {@link MembershipListener} + * instances. Usually, we have more than one instance willing to listen to + * membership messages. {@link PullPushAdapter} allows only one instance of + * {@link MembershipListener} to be registered for message notification. With + * help of this class you can overcome this limitation. + * + * @author Roman Rokytskyy (rrokytskyy@acm.org) + */ + +public class MembershipListenerAdapter implements MembershipListener { + + protected final HashSet membershipListeners = new HashSet(); + protected MembershipListener[] membershipListenersCache = + new MembershipListener[0]; + + /** + * Notify membership listeners to temporarily stop sending messages into + * a channel. This method in turn calls same method of all registered + * membership listener. + */ + public void block() { + for(int i = 0; i < membershipListenersCache.length; i++) + membershipListenersCache[i].block(); + } + + public void unblock() { + for(int i = 0; i < membershipListenersCache.length; i++) { + if(membershipListenersCache[i] instanceof ExtendedMembershipListener) + ((ExtendedMembershipListener)membershipListenersCache[i]).unblock(); + } + } + + + /** + * Notify membership listener that some node was suspected. This method + * in turn passes suspected member address to all registered membership + * listeners. + */ + public void suspect(Address suspected_mbr) { + for(int i = 0; i < membershipListenersCache.length; i++) + membershipListenersCache[i].suspect(suspected_mbr); + } + + /** + * Notify membership listener that new view was accepted. This method in + * turn passes new view to all registered membership listeners. + */ + public void viewAccepted(View new_view) { + for(int i = 0; i < membershipListenersCache.length; i++) + membershipListenersCache[i].viewAccepted(new_view); + } + + /** + * Add membership listener to this adapter. This method registers + * listener to be notified when membership event is generated. + * + * @param listener instance of {@link MembershipListener} that should be + * added to this adapter. + */ + public synchronized void addMembershipListener(MembershipListener listener) { + if (membershipListeners.add(listener)) + membershipListenersCache = + (MembershipListener[])membershipListeners.toArray( + new MembershipListener[membershipListeners.size()]); + } + + /** + * Remove membership listener from this adapter. This method deregisters + * listener from notification when membership event is generated. + * + * @param listener instance of {@link MembershipListener} that should be + * removed from this adapter. + */ + public synchronized void removeMembershipListener(MembershipListener listener) { + if (membershipListeners.remove(listener)) + membershipListenersCache = + (MembershipListener[])membershipListeners.toArray( + new MembershipListener[membershipListeners.size()]); + + } + +} \ No newline at end of file Index: 3rdParty_sources/jgroups/org/jgroups/blocks/MessageDispatcher.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/blocks/MessageDispatcher.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/blocks/MessageDispatcher.java 17 Aug 2012 14:51:18 -0000 1.1 @@ -0,0 +1,956 @@ + +package org.jgroups.blocks; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.*; +import org.jgroups.protocols.TP; +import org.jgroups.stack.Protocol; +import org.jgroups.stack.StateTransferInfo; +import org.jgroups.util.Rsp; +import org.jgroups.util.RspList; +import org.jgroups.util.Util; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.TreeSet; +import java.util.Vector; + + +/** + * Provides synchronous and asynchronous message sending with request-response + * correlation; i.e., matching responses with the original request. + * It also offers push-style message reception (by internally using the PullPushAdapter). + *

+ * Channels are simple patterns to asynchronously send a receive messages. + * However, a significant number of communication patterns in group communication + * require synchronous communication. For example, a sender would like to send a + * message to the group and wait for all responses. Or another application would + * like to send a message to the group and wait only until the majority of the + * receivers have sent a response, or until a timeout occurred. MessageDispatcher + * offers a combination of the above pattern with other patterns. + *

+ * Used on top of channel to implement group requests. Client's handle() + * method is called when request is received. Is the equivalent of RpcProtocol on + * the application instead of protocol level. + * + * @author Bela Ban + * @version $Id: MessageDispatcher.java,v 1.1 2012/08/17 14:51:18 marcin Exp $ + */ +public class MessageDispatcher implements RequestHandler { + protected Channel channel=null; + protected RequestCorrelator corr=null; + protected MessageListener msg_listener=null; + protected MembershipListener membership_listener=null; + protected RequestHandler req_handler=null; + protected ProtocolAdapter prot_adapter=null; + protected TransportAdapter transport_adapter=null; + protected final Collection members=new TreeSet(); + protected Address local_addr=null; + protected boolean deadlock_detection=false; + protected PullPushAdapter adapter=null; + protected PullPushHandler handler=null; + protected Serializable id=null; + protected final Log log=LogFactory.getLog(getClass()); + + + /** + * Process items on the queue concurrently (RequestCorrelator). The default is to wait until the processing of an + * item has completed before fetching the next item from the queue. Note that setting this to true may destroy the + * properties of a protocol stack, e.g total or causal order may not be guaranteed. Set this to true only if you + * know what you're doing ! + */ + protected boolean concurrent_processing=false; + + + public MessageDispatcher() { + } + + public MessageDispatcher(Channel channel, MessageListener l, MembershipListener l2) { + this.channel=channel; + prot_adapter=new ProtocolAdapter(); + if(channel != null) { + local_addr=channel.getLocalAddress(); + } + setMessageListener(l); + setMembershipListener(l2); + if(channel != null) { + channel.setUpHandler(prot_adapter); + } + start(); + } + + + public MessageDispatcher(Channel channel, MessageListener l, MembershipListener l2, boolean deadlock_detection) { + this.channel=channel; + this.deadlock_detection=deadlock_detection; + prot_adapter=new ProtocolAdapter(); + if(channel != null) { + local_addr=channel.getLocalAddress(); + } + setMessageListener(l); + setMembershipListener(l2); + if(channel != null) { + channel.setUpHandler(prot_adapter); + } + start(); + } + + public MessageDispatcher(Channel channel, MessageListener l, MembershipListener l2, + boolean deadlock_detection, boolean concurrent_processing) { + this.channel=channel; + this.deadlock_detection=deadlock_detection; + this.concurrent_processing=concurrent_processing; + prot_adapter=new ProtocolAdapter(); + if(channel != null) { + local_addr=channel.getLocalAddress(); + } + setMessageListener(l); + setMembershipListener(l2); + if(channel != null) { + channel.setUpHandler(prot_adapter); + } + start(); + } + + + public MessageDispatcher(Channel channel, MessageListener l, MembershipListener l2, RequestHandler req_handler) { + this(channel, l, l2); + setRequestHandler(req_handler); + } + + + public MessageDispatcher(Channel channel, MessageListener l, MembershipListener l2, RequestHandler req_handler, + boolean deadlock_detection) { + this(channel, l, l2, deadlock_detection, false); + setRequestHandler(req_handler); + } + + public MessageDispatcher(Channel channel, MessageListener l, MembershipListener l2, RequestHandler req_handler, + boolean deadlock_detection, boolean concurrent_processing) { + this(channel, l, l2, deadlock_detection, concurrent_processing); + setRequestHandler(req_handler); + } + + + /* + * Uses a user-provided PullPushAdapter rather than a Channel as transport. If id is non-null, it will be + * used to register under that id. This is typically used when another building block is already using + * PullPushAdapter, and we want to add this building block in addition. The id is the used to discriminate + * between messages for the various blocks on top of PullPushAdapter. If null, we will assume we are the + * first block created on PullPushAdapter. + * @param adapter The PullPushAdapter which to use as underlying transport + * @param id A serializable object (e.g. an Integer) used to discriminate (multiplex/demultiplex) between + * requests/responses for different building blocks on top of PullPushAdapter. + */ + public MessageDispatcher(PullPushAdapter adapter, Serializable id, + MessageListener l, MembershipListener l2) { + this.adapter=adapter; + this.id=id; + setMembers(((Channel) adapter.getTransport()).getView().getMembers()); + setMessageListener(l); + setMembershipListener(l2); + handler=new PullPushHandler(); + transport_adapter=new TransportAdapter(); + adapter.addMembershipListener(handler); // remove in stop() + if(id == null) { // no other building block around, let's become the main consumer of this PullPushAdapter + adapter.setListener(handler); + } + else { + adapter.registerListener(id, handler); + } + + Transport tp; + if((tp=adapter.getTransport()) instanceof Channel) { + local_addr=((Channel) tp).getLocalAddress(); + } + start(); + } + + + /* + * Uses a user-provided PullPushAdapter rather than a Channel as transport. If id is non-null, it will be + * used to register under that id. This is typically used when another building block is already using + * PullPushAdapter, and we want to add this building block in addition. The id is the used to discriminate + * between messages for the various blocks on top of PullPushAdapter. If null, we will assume we are the + * first block created on PullPushAdapter. + * @param adapter The PullPushAdapter which to use as underlying transport + * @param id A serializable object (e.g. an Integer) used to discriminate (multiplex/demultiplex) between + * requests/responses for different building blocks on top of PullPushAdapter. + * @param req_handler The object implementing RequestHandler. It will be called when a request is received + */ + public MessageDispatcher(PullPushAdapter adapter, Serializable id, + MessageListener l, MembershipListener l2, + RequestHandler req_handler) { + this.adapter=adapter; + this.id=id; + setMembers(((Channel) adapter.getTransport()).getView().getMembers()); + setRequestHandler(req_handler); + setMessageListener(l); + setMembershipListener(l2); + handler=new PullPushHandler(); + transport_adapter=new TransportAdapter(); + adapter.addMembershipListener(handler); + if(id == null) { // no other building block around, let's become the main consumer of this PullPushAdapter + adapter.setListener(handler); + } + else { + adapter.registerListener(id, handler); + } + + Transport tp; + if((tp=adapter.getTransport()) instanceof Channel) { + local_addr=((Channel) tp).getLocalAddress(); // fixed bug #800774 + } + + start(); + } + + + public MessageDispatcher(PullPushAdapter adapter, Serializable id, + MessageListener l, MembershipListener l2, + RequestHandler req_handler, boolean concurrent_processing) { + this.concurrent_processing=concurrent_processing; + this.adapter=adapter; + this.id=id; + setMembers(((Channel) adapter.getTransport()).getView().getMembers()); + setRequestHandler(req_handler); + setMessageListener(l); + setMembershipListener(l2); + handler=new PullPushHandler(); + transport_adapter=new TransportAdapter(); + adapter.addMembershipListener(handler); + if(id == null) { // no other building block around, let's become the main consumer of this PullPushAdapter + adapter.setListener(handler); + } + else { + adapter.registerListener(id, handler); + } + + Transport tp; + if((tp=adapter.getTransport()) instanceof Channel) { + local_addr=((Channel) tp).getLocalAddress(); // fixed bug #800774 + } + + start(); + } + + /** Returns a copy of members */ + protected Collection getMembers() { + synchronized(members) { + return new ArrayList(members); + } + } + + + /** + * If this dispatcher is using a user-provided PullPushAdapter, then need to set the members from the adapter + * initially since viewChange has most likely already been called in PullPushAdapter. + */ + private void setMembers(Vector new_mbrs) { + if(new_mbrs != null) { + synchronized(members) { + members.clear(); + members.addAll(new_mbrs); + } + } + } + + public boolean getDeadlockDetection() {return deadlock_detection;} + + public void setDeadlockDetection(boolean flag) { + deadlock_detection=flag; + if(corr != null) + corr.setDeadlockDetection(flag); + } + + + public boolean getConcurrentProcessing() {return concurrent_processing;} + + public void setConcurrentProcessing(boolean flag) { + this.concurrent_processing=flag; + } + + + public final void start() { + if(corr == null) { + if(transport_adapter != null) { + corr=new RequestCorrelator("MsgDisp", transport_adapter, + this, deadlock_detection, local_addr, concurrent_processing); + } + else { + corr=new RequestCorrelator("MsgDisp", prot_adapter, + this, deadlock_detection, local_addr, concurrent_processing); + } + } + correlatorStarted(); + corr.start(); + + if(channel != null) { + Vector tmp_mbrs=channel.getView() != null ? channel.getView().getMembers() : null; + setMembers(tmp_mbrs); + if(channel instanceof JChannel) { + TP transport=((JChannel)channel).getProtocolStack().getTransport(); + corr.registerProbeHandler(transport); + } + } + } + + protected void correlatorStarted() { + ; + } + + + public void stop() { + if(corr != null) { + corr.stop(); + } + + if(channel instanceof JChannel) { + TP transport=((JChannel)channel).getProtocolStack().getTransport(); + corr.unregisterProbeHandler(transport); + } + + // fixes leaks of MembershipListeners (http://jira.jboss.com/jira/browse/JGRP-160) + if(adapter != null && handler != null) { + adapter.removeMembershipListener(handler); + } + } + + + public final void setMessageListener(MessageListener l) { + msg_listener=l; + } + + /** + * Gives access to the currently configured MessageListener. Returns null if there is no + * configured MessageListener. + */ + public MessageListener getMessageListener() { + return msg_listener; + } + + public final void setMembershipListener(MembershipListener l) { + membership_listener=l; + } + + public final void setRequestHandler(RequestHandler rh) { + req_handler=rh; + } + + /** + * Offers access to the underlying Channel. + * @return a reference to the underlying Channel. + */ + public Channel getChannel() { + return channel; + } + + public void setChannel(Channel ch) { + if(ch == null) + return; + this.channel=ch; + local_addr=channel.getLocalAddress(); + if(prot_adapter == null) + prot_adapter=new ProtocolAdapter(); + channel.setUpHandler(prot_adapter); + } + + + public void send(Message msg) throws ChannelNotConnectedException, ChannelClosedException { + if(channel != null) { + channel.send(msg); + } + else + if(adapter != null) { + try { + if(id != null) { + adapter.send(id, msg); + } + else { + adapter.send(msg); + } + } + catch(Throwable ex) { + if(log.isErrorEnabled()) { + log.error("exception=" + Util.print(ex)); + } + } + } + else { + if(log.isErrorEnabled()) { + log.error("channel == null"); + } + } + } + + + public RspList castMessage(final Vector dests, Message msg, int mode, long timeout) { + return castMessage(dests, msg, mode, timeout, false); + } + + + /** + * Cast a message to all members, and wait for mode responses. The responses are returned in a response + * list, where each response is associated with its sender.

Uses GroupRequest. + * + * @param dests The members to which the message is to be sent. If it is null, then the message is sent to all + * members + * @param msg The message to be sent to n members + * @param mode Defined in GroupRequest. The number of responses to wait for:

  1. GET_FIRST: + * return the first response received.
  2. GET_ALL: wait for all responses (minus the ones from + * suspected members)
  3. GET_MAJORITY: wait for a majority of all responses (relative to the grp + * size)
  4. GET_ABS_MAJORITY: wait for majority (absolute, computed once)
  5. GET_N: wait for n + * responses (may block if n > group size)
  6. GET_NONE: wait for no responses, return immediately + * (non-blocking)
+ * @param timeout If 0: wait forever. Otherwise, wait for mode responses or timeout time. + * @return RspList A list of responses. Each response is an Object and associated to its sender. + */ + public RspList castMessage(final Vector dests, Message msg, int mode, long timeout, boolean use_anycasting) { + return castMessage(dests, msg, mode, timeout, use_anycasting, null); + } + + + public RspList castMessage(final Vector dests, Message msg, int mode, long timeout, boolean use_anycasting, + RspFilter filter) { + GroupRequest _req=null; + Vector real_dests; + Channel tmp; + + // we need to clone because we don't want to modify the original + // (we remove ourselves if LOCAL is false, see below) ! + // real_dests=dests != null ? (Vector) dests.clone() : (members != null ? new Vector(members) : null); + if(dests != null) { + real_dests=(Vector)dests.clone(); + real_dests.retainAll(this.members); + } + else { + synchronized(members) { + real_dests=new Vector(members); + } + } + + // if local delivery is off, then we should not wait for the message from the local member. + // therefore remove it from the membership + tmp=channel; + if(tmp == null) { + if(adapter != null && adapter.getTransport() instanceof Channel) { + tmp=(Channel) adapter.getTransport(); + } + } + + if(tmp != null && tmp.getOpt(Channel.LOCAL).equals(Boolean.FALSE)) { + if(local_addr == null) { + local_addr=tmp.getLocalAddress(); + } + if(local_addr != null) { + real_dests.removeElement(local_addr); + } + } + + // don't even send the message if the destination list is empty + if(log.isTraceEnabled()) + log.trace("real_dests=" + real_dests); + + if(real_dests.isEmpty()) { + if(log.isTraceEnabled()) + log.trace("destination list is empty, won't send message"); + return new RspList(); // return empty response list + } + + _req=new GroupRequest(msg, corr, real_dests, mode, timeout, 0); + _req.setCaller(this.local_addr); + _req.setResponseFilter(filter); + try { + _req.execute(use_anycasting); + } + catch(Exception ex) { + throw new RuntimeException("failed executing request " + _req, ex); + } + + return _req.getResults(); + } + + + /** + * Multicast a message request to all members in dests and receive responses via the RspCollector + * interface. When done receiving the required number of responses, the caller has to call done(req_id) on the + * underlyinh RequestCorrelator, so that the resources allocated to that request can be freed. + * + * @param dests The list of members from which to receive responses. Null means all members + * @param req_id The ID of the request. Used by the underlying RequestCorrelator to correlate responses with + * requests + * @param msg The request to be sent + * @param coll The sender needs to provide this interface to collect responses. Call will return immediately if + * this is null + */ + public void castMessage(final Vector dests, long req_id, Message msg, RspCollector coll) { + Vector real_dests; + Channel tmp; + + if(msg == null) { + if(log.isErrorEnabled()) + log.error("request is null"); + return; + } + + if(coll == null) { + if(log.isErrorEnabled()) + log.error("response collector is null (must be non-null)"); + return; + } + + // we need to clone because we don't want to modify the original + // (we remove ourselves if LOCAL is false, see below) ! + //real_dests=dests != null ? (Vector) dests.clone() : (Vector) members.clone(); + if(dests != null) { + real_dests=(Vector)dests.clone(); + real_dests.retainAll(this.members); + } + else { + synchronized(members) { + real_dests=new Vector(members); + } + } + + // if local delivery is off, then we should not wait for the message from the local member. + // therefore remove it from the membership + tmp=channel; + if(tmp == null) { + if(adapter != null && adapter.getTransport() instanceof Channel) { + tmp=(Channel) adapter.getTransport(); + } + } + + if(tmp != null && tmp.getOpt(Channel.LOCAL).equals(Boolean.FALSE)) { + if(local_addr == null) { + local_addr=tmp.getLocalAddress(); + } + if(local_addr != null) { + real_dests.removeElement(local_addr); + } + } + + // don't even send the message if the destination list is empty + if(real_dests.isEmpty()) { + if(log.isDebugEnabled()) + log.debug("destination list is empty, won't send message"); + return; + } + + try { + corr.sendRequest(req_id, real_dests, msg, coll); + } + catch(Exception e) { + throw new RuntimeException("failure sending request " + req_id + " to " + real_dests, e); + } + } + + + public void done(long req_id) { + corr.done(req_id); + } + + + /** + * Sends a message to a single member (destination = msg.dest) and returns the response. The message's destination + * must be non-zero ! + */ + public Object sendMessage(Message msg, int mode, long timeout) throws TimeoutException, SuspectedException { + Vector mbrs=new Vector(); + RspList rsp_list=null; + Object dest=msg.getDest(); + Rsp rsp; + GroupRequest _req=null; + + if(dest == null) { + if(log.isErrorEnabled()) + log.error("the message's destination is null, cannot send message"); + return null; + } + + mbrs.addElement(dest); // dummy membership (of destination address) + + _req=new GroupRequest(msg, corr, mbrs, mode, timeout, 0); + _req.setCaller(local_addr); + try { + _req.execute(); + } + catch(Exception t) { + throw new RuntimeException("failed executing request " + _req, t); + } + + if(mode == GroupRequest.GET_NONE) { + return null; + } + + rsp_list=_req.getResults(); + + if(rsp_list.isEmpty()) { + if(log.isWarnEnabled()) + log.warn(" response list is empty"); + return null; + } + if(rsp_list.size() > 1) { + if(log.isWarnEnabled()) + log.warn("response list contains more that 1 response; returning first response !"); + } + rsp=(Rsp)rsp_list.elementAt(0); + if(rsp.wasSuspected()) { + throw new SuspectedException(dest); + } + if(!rsp.wasReceived()) { + throw new TimeoutException("timeout sending message to " + dest); + } + return rsp.getValue(); + } + + + + /* ------------------------ RequestHandler Interface ---------------------- */ + public Object handle(Message msg) { + if(req_handler != null) { + return req_handler.handle(msg); + } + else { + return null; + } + } + /* -------------------- End of RequestHandler Interface ------------------- */ + + + + + + + class ProtocolAdapter extends Protocol implements UpHandler { + + + /* ------------------------- Protocol Interface --------------------------- */ + + public String getName() { + return "MessageDispatcher"; + } + + + + private Object handleUpEvent(Event evt) { + switch(evt.getType()) { + case Event.MSG: + if(msg_listener != null) { + msg_listener.receive((Message) evt.getArg()); + } + break; + + case Event.GET_APPLSTATE: // reply with GET_APPLSTATE_OK + StateTransferInfo info=(StateTransferInfo)evt.getArg(); + String state_id=info.state_id; + byte[] tmp_state=null; + if(msg_listener != null) { + try { + if(msg_listener instanceof ExtendedMessageListener && state_id!=null) { + tmp_state=((ExtendedMessageListener)msg_listener).getState(state_id); + } + else { + tmp_state=msg_listener.getState(); + } + } + catch(Throwable t) { + this.log.error("failed getting state from message listener (" + msg_listener + ')', t); + } + } + return new StateTransferInfo(null, state_id, 0L, tmp_state); + + case Event.GET_STATE_OK: + if(msg_listener != null) { + try { + info=(StateTransferInfo)evt.getArg(); + String id=info.state_id; + if(msg_listener instanceof ExtendedMessageListener && id!=null) { + ((ExtendedMessageListener)msg_listener).setState(id, info.state); + } + else { + msg_listener.setState(info.state); + } + } + catch(ClassCastException cast_ex) { + if(this.log.isErrorEnabled()) + this.log.error("received SetStateEvent, but argument " + + evt.getArg() + " is not serializable. Discarding message."); + } + } + break; + + case Event.STATE_TRANSFER_OUTPUTSTREAM: + StateTransferInfo sti=(StateTransferInfo)evt.getArg(); + OutputStream os=sti.outputStream; + if(msg_listener instanceof ExtendedMessageListener) { + if(os != null && msg_listener instanceof ExtendedMessageListener) { + if(sti.state_id == null) + ((ExtendedMessageListener)msg_listener).getState(os); + else + ((ExtendedMessageListener)msg_listener).getState(sti.state_id, os); + } + return new StateTransferInfo(null, os, sti.state_id); + } + else if(msg_listener instanceof MessageListener){ + if(log.isWarnEnabled()){ + log.warn("Channel has STREAMING_STATE_TRANSFER, however," + + " application does not implement ExtendedMessageListener. State is not transfered"); + Util.close(os); + } + } + break; + + case Event.STATE_TRANSFER_INPUTSTREAM: + sti=(StateTransferInfo)evt.getArg(); + InputStream is=sti.inputStream; + if(msg_listener instanceof ExtendedMessageListener) { + if(is!=null && msg_listener instanceof ExtendedMessageListener) { + if(sti.state_id == null) + ((ExtendedMessageListener)msg_listener).setState(is); + else + ((ExtendedMessageListener)msg_listener).setState(sti.state_id, is); + } + } + else if(msg_listener instanceof MessageListener){ + if(log.isWarnEnabled()){ + log.warn("Channel has STREAMING_STATE_TRANSFER, however," + + " application does not implement ExtendedMessageListener. State is not transfered"); + Util.close(is); + } + } + break; + + case Event.VIEW_CHANGE: + View v=(View) evt.getArg(); + Vector new_mbrs=v.getMembers(); + setMembers(new_mbrs); + if(membership_listener != null) { + membership_listener.viewAccepted(v); + } + break; + + case Event.SET_LOCAL_ADDRESS: + if(log.isTraceEnabled()) + log.trace("setting local_addr (" + local_addr + ") to " + evt.getArg()); + local_addr=(Address)evt.getArg(); + break; + + case Event.SUSPECT: + if(membership_listener != null) { + membership_listener.suspect((Address) evt.getArg()); + } + break; + + case Event.BLOCK: + if(membership_listener != null) { + membership_listener.block(); + } + channel.blockOk(); + break; + case Event.UNBLOCK: + if(membership_listener instanceof ExtendedMembershipListener) { + ((ExtendedMembershipListener)membership_listener).unblock(); + } + break; + } + + return null; + } + + + + + + + /** + * Called by channel (we registered before) when event is received. This is the UpHandler interface. + */ + public Object up(Event evt) { + if(corr != null) { + if(!corr.receive(evt)) { + return handleUpEvent(evt); + } + } + else { + if(log.isErrorEnabled()) { //Something is seriously wrong, correlator should not be null since latch is not locked! + log.error("correlator is null, event will be ignored (evt=" + evt + ")"); + } + } + return null; + } + + + + public Object down(Event evt) { + if(channel != null) { + return channel.downcall(evt); + } + else + if(this.log.isWarnEnabled()) { + this.log.warn("channel is null, discarding event " + evt); + } + return null; + } + + + /* ----------------------- End of Protocol Interface ------------------------ */ + + } + + + class TransportAdapter implements Transport { + + public void send(Message msg) throws Exception { + if(channel != null) { + channel.send(msg); + } + else + if(adapter != null) { + try { + if(id != null) { + adapter.send(id, msg); + } + else { + adapter.send(msg); + } + } + catch(Throwable ex) { + if(log.isErrorEnabled()) { + log.error("exception=" + Util.print(ex)); + } + } + } + else { + if(log.isErrorEnabled()) { + log.error("channel == null"); + } + } + } + + public Object receive(long timeout) throws Exception { + return null; // not supported and not needed + } + } + + + class PullPushHandler implements ExtendedMessageListener, MembershipListener { + + + /* ------------------------- MessageListener interface ---------------------- */ + public void receive(Message msg) { + boolean consumed=false; + if(corr != null) { + consumed=corr.receiveMessage(msg); + } + + if(!consumed) { // pass on to MessageListener + if(msg_listener != null) { + msg_listener.receive(msg); + } + } + } + + public byte[] getState() { + return msg_listener != null ? msg_listener.getState() : null; + } + + public byte[] getState(String state_id) { + if(msg_listener == null) return null; + if(msg_listener instanceof ExtendedMessageListener && state_id!=null) { + return ((ExtendedMessageListener)msg_listener).getState(state_id); + } + else { + return msg_listener.getState(); + } + } + + public void setState(byte[] state) { + if(msg_listener != null) { + msg_listener.setState(state); + } + } + + public void setState(String state_id, byte[] state) { + if(msg_listener != null) { + if(msg_listener instanceof ExtendedMessageListener && state_id!=null) { + ((ExtendedMessageListener)msg_listener).setState(state_id, state); + } + else { + msg_listener.setState(state); + } + } + } + + public void getState(OutputStream ostream) { + if (msg_listener instanceof ExtendedMessageListener) { + ((ExtendedMessageListener) msg_listener).getState(ostream); + } + } + + public void getState(String state_id, OutputStream ostream) { + if (msg_listener instanceof ExtendedMessageListener && state_id!=null) { + ((ExtendedMessageListener) msg_listener).getState(state_id,ostream); + } + + } + + public void setState(InputStream istream) { + if (msg_listener instanceof ExtendedMessageListener) { + ((ExtendedMessageListener) msg_listener).setState(istream); + } + } + + public void setState(String state_id, InputStream istream) { + if (msg_listener instanceof ExtendedMessageListener && state_id != null) { + ((ExtendedMessageListener) msg_listener).setState(state_id,istream); + } + } + /* + * --------------------- End of MessageListener interface + * ------------------- + */ + + + /* ------------------------ MembershipListener interface -------------------- */ + public void viewAccepted(View v) { + if(corr != null) { + corr.receiveView(v); + } + + Vector new_mbrs=v.getMembers(); + setMembers(new_mbrs); + if(membership_listener != null) { + membership_listener.viewAccepted(v); + } + } + + public void suspect(Address suspected_mbr) { + if(corr != null) { + corr.receiveSuspect(suspected_mbr); + } + if(membership_listener != null) { + membership_listener.suspect(suspected_mbr); + } + } + + public void block() { + if(membership_listener != null) { + membership_listener.block(); + } + } + + /* --------------------- End of MembershipListener interface ---------------- */ + + + + // @todo: receive SET_LOCAL_ADDR event and call corr.setLocalAddress(addr) + + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/blocks/MessageListenerAdapter.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/blocks/MessageListenerAdapter.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/blocks/MessageListenerAdapter.java 17 Aug 2012 14:51:18 -0000 1.1 @@ -0,0 +1,170 @@ +package org.jgroups.blocks; + +import org.jgroups.Message; +import org.jgroups.MessageListener; +import org.jgroups.ExtendedMessageListener; + +import java.io.InputStream; +import java.io.OutputStream; +import java.util.HashSet; + +/** + * This class provides multiplexing possibilities for {@link MessageListener} + * instances. Usually, we have more than one instance willing to listen to + * incoming messages, but only one that can produce state for group. + * {@link PullPushAdapter} allows only one instance of {@link MessageListener} + * to be registered for message notification. With help of this class you + * can overcome this limitation. + * + * @author Roman Rokytskyy (rrokytskyy@acm.org) + */ +public class MessageListenerAdapter implements ExtendedMessageListener { + + protected MessageListener stateListener; + + protected final HashSet messageListeners = new HashSet(); + + // we need this cache, because every call to messageListeners.iterator() + // would generate few new objects, but iteration over the cache would not. + protected MessageListener[] messageListenersCache = new MessageListener[0]; + + /** + * Create default instance of this class. Newly created instance will have + * no message or state listeners. You have to use + * {@link #addMessageListener(MessageListener)} or + * {@link #removeMessageListener(MessageListener)} to add or remove message + * listeners, and {@link #setStateListener(MessageListener)} to set listener + * that will participate in state transfer. + */ + public MessageListenerAdapter() { + this(null); + } + + /** + * Create instance of this class. mainListener is a main + * listener instance that received message notifications and can get and + * set group state. + * + * @param mainListener instance of {@link MessageListener} that will + * provide state messages. + */ + public MessageListenerAdapter(MessageListener mainListener) { + if (mainListener != null) { + stateListener = mainListener; + addMessageListener(mainListener); + } + } + + /** + * Get state from state listener if present. + * + * @return current state of the group state or null if no state + * listeners were registered. + */ + public byte[] getState() { + if (stateListener != null) + return stateListener.getState(); + else + return null; + } + + + public byte[] getState(String state_id) { + if(stateListener == null) + return null; + if(stateListener instanceof ExtendedMessageListener) { + return ((ExtendedMessageListener)stateListener).getState(state_id); + } + else { + return stateListener.getState(); + } + } + + public void getState(OutputStream ostream) { + if (stateListener instanceof ExtendedMessageListener) { + ((ExtendedMessageListener) stateListener).getState(ostream); + } + } + + public void getState(String state_id, OutputStream ostream) { + if (stateListener instanceof ExtendedMessageListener && state_id!=null) { + ((ExtendedMessageListener) stateListener).getState(state_id,ostream); + } + } + + /** + * Receive message from group. This method will send this message to each + * message listener that was registered in this adapter. + * + * @param msg message to distribute within message listeners. + */ + public void receive(Message msg) { + for (int i = 0; i < messageListenersCache.length; i++) + messageListenersCache[i].receive(msg); + } + + /** + * Set state of ths group. This method will delegate call to state listener + * if it was previously registered. + */ + public void setState(byte[] state) { + if (stateListener != null) + stateListener.setState(state); + } + + public void setState(String state_id, byte[] state) { + if(stateListener != null) { + if(stateListener instanceof ExtendedMessageListener) { + ((ExtendedMessageListener)stateListener).setState(state_id, state); + } + else { + stateListener.setState(state); + } + } + } + + + public void setState(InputStream istream) { + if (stateListener instanceof ExtendedMessageListener) { + ((ExtendedMessageListener) stateListener).setState(istream); + } + } + + public void setState(String state_id, InputStream istream) { + if (stateListener instanceof ExtendedMessageListener && state_id != null) { + ((ExtendedMessageListener) stateListener).setState(state_id,istream); + } + } + + /** + * Add message listener to this adapter. This method registers + * listener for message notification. + *

+ * Note, state notification will not be used. + */ + public final synchronized void addMessageListener(MessageListener listener) { + if (messageListeners.add(listener)) + messageListenersCache = + (MessageListener[])messageListeners.toArray( + new MessageListener[messageListeners.size()]); + } + + /** + * Remove message listener from this adapter. This method deregisters + * listener from message notification. + */ + public synchronized void removeMessageListener(MessageListener listener) { + if (messageListeners.remove(listener)) + messageListenersCache = + (MessageListener[])messageListeners.toArray( + new MessageListener[messageListeners.size()]); + } + + /** + * Register listener for state notification events. There can + * be only one state listener per adapter. + */ + public void setStateListener(MessageListener listener) { + stateListener = listener; + } +} Index: 3rdParty_sources/jgroups/org/jgroups/blocks/MethodCall.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/blocks/MethodCall.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/blocks/MethodCall.java 17 Aug 2012 14:51:17 -0000 1.1 @@ -0,0 +1,527 @@ +package org.jgroups.blocks; + + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.*; + + +/** + * A method call is the JGroups representation of a remote method. + * It includes the name of the method (case sensitive) and a list of arguments. + * A method call is serializable and can be passed over the wire. + * @author Bela Ban + * @version $Revision: 1.1 $ + */ +public class MethodCall implements Externalizable { + + private static final long serialVersionUID=7873471327078957662L; + + /** The name of the method, case sensitive. */ + protected String method_name; + + /** The ID of a method, maps to a java.lang.reflect.Method */ + protected short method_id=-1; + + /** The arguments of the method. */ + protected Object[] args; + + /** The class types, e.g., new Class[]{String.class, int.class}. */ + protected Class[] types; + + /** The signature, e.g., new String[]{String.class.getName(), int.class.getName()}. */ + protected String[] signature; + + /** The Method of the call. */ + protected Method method; + + /** To carry arbitrary data with a method call, data needs to be serializable if sent across the wire */ + protected Map payload; + + protected static final Log log=LogFactory.getLog(MethodCall.class); + + /** Which mode to use. */ + protected short mode=OLD; + + /** Infer the method from the arguments. */ + protected static final short OLD=1; + + /** Explicitly ship the method, caller has to determine method himself. */ + protected static final short METHOD=2; + + /** Use class information. */ + protected static final short TYPES=3; + + /** Provide a signature, similar to JMX. */ + protected static final short SIGNATURE=4; + + /** Use an ID to map to a method */ + protected static final short ID=5; + + + /** + * Creates an empty method call, this is always invalid, until + * setName() has been called. + */ + public MethodCall() { + } + + + public MethodCall(Method method) { + this(method, null); + } + + public MethodCall(Method method, Object[] arguments) { + init(method); + if(arguments != null) args=arguments; + } + + /** + * + * @param method_name + * @param args + * @deprecated Use one of the constructors that take class types as arguments + */ + public MethodCall(String method_name, Object[] args) { + this.method_name=method_name; + this.mode=OLD; + this.args=args; + } + + public MethodCall(short method_id, Object[] args) { + this.method_id=method_id; + this.mode=ID; + this.args=args; + } + + + public MethodCall(String method_name, Object[] args, Class[] types) { + this.method_name=method_name; + this.args=args; + this.types=types; + this.mode=TYPES; + } + + public MethodCall(String method_name, Object[] args, String[] signature) { + this.method_name=method_name; + this.args=args; + this.signature=signature; + this.mode=SIGNATURE; + } + + private void init(Method method) { + this.method=method; + this.mode=METHOD; + method_name=method.getName(); + } + + + public int getMode() { + return mode; + } + + + + /** + * returns the name of the method to be invoked using this method call object + * @return a case sensitive name, can be null for an invalid method call + */ + public String getName() { + return method_name; + } + + /** + * sets the name for this MethodCall and allowing you to reuse the same object for + * a different method invokation of a different method + * @param n - a case sensitive method name + */ + public void setName(String n) { + method_name=n; + } + + public short getId() { + return method_id; + } + + public void setId(short method_id) { + this.method_id=method_id; + } + + /** + * returns an ordered list of arguments used for the method invokation + * @return returns the list of ordered arguments + */ + public Object[] getArgs() { + return args; + } + + public void setArgs(Object[] args) { + if(args != null) + this.args=args; + } + + public Method getMethod() { + return method; + } + + + public void setMethod(Method m) { + init(m); + } + + + public synchronized Object put(Object key, Object value) { + if(payload == null) + payload=new HashMap(); + return payload.put(key, value); + } + + public synchronized Object get(Object key) { + return payload != null? payload.get(key) : null; + } + + + /** + * + * @param target_class + * @return + * @throws Exception + */ + Method findMethod(Class target_class) throws Exception { + int len=args != null? args.length : 0; + Method m; + + Method[] methods=getAllMethods(target_class); + for(int i=0; i < methods.length; i++) { + m=methods[i]; + if(m.getName().equals(method_name)) { + if(m.getParameterTypes().length == len) + return m; + } + } + + return null; + } + + + /** + * The method walks up the class hierarchy and returns all methods of this class + * and those inherited from superclasses and superinterfaces. + */ + Method[] getAllMethods(Class target) { + Class superclass = target; + List methods = new ArrayList(); + int size = 0; + + while(superclass != null) { + try { + Method[] m = superclass.getDeclaredMethods(); + methods.add(m); + size += m.length; + superclass = superclass.getSuperclass(); + } + catch(SecurityException e) { + // if it runs in an applet context, it won't be able to retrieve + // methods from superclasses that belong to the java VM and it will + // raise a security exception, so we catch it here. + if(log.isWarnEnabled()) + log.warn("unable to enumerate methods of superclass "+superclass+" of class "+target); + superclass=null; + } + } + + Method[] result = new Method[size]; + int index = 0; + for(Iterator i = methods.iterator(); i.hasNext();) { + Method[] m = (Method[])i.next(); + System.arraycopy(m, 0, result, index, m.length); + index += m.length; + } + return result; + } + + /** + * Returns the first method that matches the specified name and parameter types. The overriding + * methods have priority. The method is chosen from all the methods of the current class and all + * its superclasses and superinterfaces. + * + * @return the matching method or null if no mathching method has been found. + */ + Method getMethod(Class target, String methodName, Class[] types) { + + if (types == null) { + types = new Class[0]; + } + + Method[] methods = getAllMethods(target); + methods: for(int i = 0; i < methods.length; i++) { + Method m = methods[i]; + if (!methodName.equals(m.getName())) { + continue; + } + Class[] parameters = m.getParameterTypes(); + if (types.length != parameters.length) { + continue; + } + for(int j = 0; j < types.length; j++) { + if(!parameters[j].isAssignableFrom(types[j])) { + // if (!types[j].equals(parameters[j])) { + continue methods; + } + } + return m; + } + return null; + } + + + /** + * Invokes the method with the supplied arguments against the target object. + * If a method lookup is provided, it will be used. Otherwise, the default + * method lookup will be used. + * @param target - the object that you want to invoke the method on + * @return an object + */ + public Object invoke(Object target) throws Throwable { + Class cl; + Method meth=null; + Object retval=null; + + + if(method_name == null || target == null) { + if(log.isErrorEnabled()) log.error("method name or target is null"); + return null; + } + cl=target.getClass(); + try { + switch(mode) { + case OLD: + meth=findMethod(cl); + break; + case METHOD: + if(this.method != null) + meth=this.method; + break; + case TYPES: + //meth=cl.getDeclaredMethod(method_name, types); + meth = getMethod(cl, method_name, types); + break; + case SIGNATURE: + Class[] mytypes=null; + if(signature != null) + mytypes=getTypesFromString(cl, signature); + //meth=cl.getDeclaredMethod(method_name, mytypes); + meth = getMethod(cl, method_name, mytypes); + break; + case ID: + break; + default: + if(log.isErrorEnabled()) log.error("mode " + mode + " is invalid"); + break; + } + + if(meth != null) { + retval=meth.invoke(target, args); + } + else { + throw new NoSuchMethodException(method_name); + } + return retval; + } + catch(InvocationTargetException inv_ex) { + throw inv_ex.getTargetException(); + } + catch(NoSuchMethodException no) { + StringBuilder sb=new StringBuilder(); + sb.append("found no method called ").append(method_name).append(" in class "); + sb.append(cl.getName()).append(" with ("); + if(args != null) { + for(int i=0; i < args.length; i++) { + if(i > 0) + sb.append(", "); + sb.append((args[i] != null)? args[i].getClass().getName() : "null"); + } + } + sb.append(") formal parameters"); + log.error(sb.toString()); + throw no; + } + catch(Throwable e) { + // e.printStackTrace(System.err); + if(log.isErrorEnabled()) log.error("exception in invoke()", e); + throw e; + } + } + + public Object invoke(Object target, Object[] args) throws Throwable { + if(args != null) + this.args=args; + return invoke(target); + } + + + Class[] getTypesFromString(Class cl, String[] signature) throws Exception { + String name; + Class parameter; + Class[] mytypes=new Class[signature.length]; + + for(int i=0; i < signature.length; i++) { + name=signature[i]; + if("long".equals(name)) + parameter=long.class; + else if("int".equals(name)) + parameter=int.class; + else if("short".equals(name)) + parameter=short.class; + else if("char".equals(name)) + parameter=char.class; + else if("byte".equals(name)) + parameter=byte.class; + else if("float".equals(name)) + parameter=float.class; + else if("double".equals(name)) + parameter=double.class; + else if("boolean".equals(name)) + parameter=boolean.class; + else + parameter=Class.forName(name, false, cl.getClassLoader()); + mytypes[i]=parameter; + } + return mytypes; + } + + + public String toString() { + StringBuilder ret=new StringBuilder(); + boolean first=true; + if(method_name != null) + ret.append(method_name); + else + ret.append(method_id); + ret.append('('); + if(args != null) { + for(int i=0; i < args.length; i++) { + if(first) + first=false; + else + ret.append(", "); + ret.append(args[i]); + } + } + ret.append(')'); + return ret.toString(); + } + + public String toStringDetails() { + StringBuilder ret=new StringBuilder(); + ret.append("MethodCall "); + if(method_name != null) + ret.append("name=").append(method_name); + else + ret.append("id=").append(method_id); + ret.append(", number of args=").append((args != null? args.length : 0)).append(')'); + if(args != null) { + ret.append("\nArgs:"); + for(int i=0; i < args.length; i++) { + ret.append("\n[").append(args[i]).append(" ("). + append((args[i] != null? args[i].getClass().getName() : "null")).append(")]"); + } + } + return ret.toString(); + } + + + public void writeExternal(ObjectOutput out) throws IOException { + if(method_name != null) { + out.writeBoolean(true); + out.writeUTF(method_name); + } + else { + out.writeBoolean(false); + out.writeShort(method_id); + } + out.writeObject(args); + out.writeShort(mode); + + switch(mode) { + case OLD: + break; + case METHOD: + out.writeObject(method.getParameterTypes()); + out.writeObject(method.getDeclaringClass()); + break; + case TYPES: + out.writeObject(types); + break; + case SIGNATURE: + out.writeObject(signature); + break; + case ID: + break; + default: + if(log.isErrorEnabled()) log.error("mode " + mode + " is invalid"); + break; + } + + if(payload != null) { + out.writeBoolean(true); + out.writeObject(payload); + } + else { + out.writeBoolean(false); + } + } + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + boolean name_available=in.readBoolean(); + if(name_available) + method_name=in.readUTF(); + else + method_id=in.readShort(); + args=(Object[])in.readObject(); + mode=in.readShort(); + + switch(mode) { + case OLD: + break; + case METHOD: + Class[] parametertypes=(Class[])in.readObject(); + Class declaringclass=(Class)in.readObject(); + try { + method=declaringclass.getDeclaredMethod(method_name, parametertypes); + } + catch(NoSuchMethodException e) { + throw new IOException(e.toString()); + } + break; + case TYPES: + types=(Class[])in.readObject(); + break; + case SIGNATURE: + signature=(String[])in.readObject(); + break; + case ID: + break; + default: + if(log.isErrorEnabled()) log.error("mode " + mode + " is invalid"); + break; + } + + boolean payload_available=in.readBoolean(); + if(payload_available) { + payload=(Map)in.readObject(); + } + } + + +} + + + Index: 3rdParty_sources/jgroups/org/jgroups/blocks/MethodLookup.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/blocks/MethodLookup.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/blocks/MethodLookup.java 17 Aug 2012 14:51:18 -0000 1.1 @@ -0,0 +1,11 @@ +package org.jgroups.blocks; + +import java.lang.reflect.Method; + +/** + * @author Bela Ban + * @version $Id: MethodLookup.java,v 1.1 2012/08/17 14:51:18 marcin Exp $ + */ +public interface MethodLookup { + Method findMethod(short id); +} Index: 3rdParty_sources/jgroups/org/jgroups/blocks/NBMessageForm_NIO.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/blocks/NBMessageForm_NIO.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/blocks/NBMessageForm_NIO.java 17 Aug 2012 14:51:18 -0000 1.1 @@ -0,0 +1,90 @@ +// $Id: NBMessageForm_NIO.java,v 1.1 2012/08/17 14:51:18 marcin Exp $ + +package org.jgroups.blocks; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.SocketChannel; + +/** + * NBMessageForm - Message form for non-blocking message reads. + * @author akbollu + * @author Bela Ban + */ +public class NBMessageForm_NIO +{ + ByteBuffer headerBuffer = null; + ByteBuffer dataBuffer = null; + static final int HEADER_SIZE = 4; + final boolean isComplete = false; + int messageSize = 0; + boolean w_in_p = false; + SocketChannel channel = null; + + + + public NBMessageForm_NIO(int dataBuffSize, SocketChannel ch) + { + headerBuffer = ByteBuffer.allocate(HEADER_SIZE); + dataBuffer = ByteBuffer.allocate(dataBuffSize); + channel = ch; + } + + + + public ByteBuffer readCompleteMsgBuffer() throws IOException + { + + int rt; + + try { + rt = channel.read(headerBuffer); + if ( (rt == 0) || (rt == -1) ) + { + channel.close(); + return null; + } + if (rt == HEADER_SIZE) + { + headerBuffer.flip(); + messageSize = headerBuffer.getInt(); + if(dataBuffer.capacity() < messageSize) + { + dataBuffer = ByteBuffer.allocate(messageSize); + } + } + else { + return null; + } + } + catch(IOException ex) { + channel.close(); + throw ex; + } + + + //rt == 0 need not be checked twice in the same event + channel.read(dataBuffer); + if(isComplete()) + { + dataBuffer.flip(); + return dataBuffer; + } + return null; + } + + + + public void reset() + { + dataBuffer.clear(); + headerBuffer.clear(); + messageSize = 0; + w_in_p = false; + } + + private boolean isComplete() + { + return ( dataBuffer.position() == messageSize ); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/blocks/NotificationBus.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/blocks/NotificationBus.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/blocks/NotificationBus.java 17 Aug 2012 14:51:18 -0000 1.1 @@ -0,0 +1,449 @@ +// $Id: NotificationBus.java,v 1.1 2012/08/17 14:51:18 marcin Exp $ + +package org.jgroups.blocks; + + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.*; +import org.jgroups.util.Promise; +import org.jgroups.util.Util; + +import java.io.Serializable; +import java.util.Vector; + + +/** + * This class provides notification sending and handling capability. + * Producers can send notifications to all registered consumers. + * Provides hooks to implement shared group state, which allows an + * application programmer to maintain a local cache which is replicated + * by all instances. NotificationBus sits on + * top of a channel, however it creates its channel itself, so the + * application programmers do not have to provide their own channel. + * + * @author Bela Ban + */ +public class NotificationBus implements Receiver { + final Vector members=new Vector(); + Channel channel=null; + Address local_addr=null; + Consumer consumer=null; // only a single consumer allowed + String bus_name="notification_bus"; + final Promise get_cache_promise=new Promise(); + final Object cache_mutex=new Object(); + + protected final Log log=LogFactory.getLog(getClass()); + + + String props=null; + + + public interface Consumer { + void handleNotification(Serializable n); + + /** Called on the coordinator to obtains its cache */ + Serializable getCache(); + + void memberJoined(Address mbr); + + void memberLeft(Address mbr); + } + + + public NotificationBus() throws Exception { + this((Channel)null, null); + } + + + public NotificationBus(String bus_name) throws Exception { + this(bus_name, null); + } + + + public NotificationBus(String bus_name, String properties) throws Exception { + if(bus_name != null) this.bus_name=bus_name; + if(properties != null) props=properties; + channel=new JChannel(props); + channel.setReceiver(this); + } + + public NotificationBus(Channel channel, String bus_name) throws Exception { + if(bus_name != null) this.bus_name=bus_name; + this.channel=channel; + if(channel != null) + channel.setReceiver(this); + } + + + public void setConsumer(Consumer c) { + consumer=c; + } + + + public Address getLocalAddress() { + if(local_addr != null) return local_addr; + if(channel != null) + local_addr=channel.getLocalAddress(); + return local_addr; + } + + + /** + * Returns a reference to the real membership: don't modify. + * If you need to modify, make a copy first ! + * @return Vector of Address objects + */ + public Vector getMembership() { + return members; + } + + + /** + * Answers the Channel. + * Used to operate on the underlying channel directly, e.g. perform operations that are not + * provided using only NotificationBus. Should be used sparingly. + * @return underlying Channel + */ + public Channel getChannel() { + return channel; + } + + + public boolean isCoordinator() { + Object first_mbr=null; + + synchronized(members) { + first_mbr=!members.isEmpty()? members.elementAt(0) : null; + if(first_mbr == null) + return true; + } + return getLocalAddress() != null && getLocalAddress().equals(first_mbr); + } + + + public void start() throws Exception { + channel.connect(bus_name); + } + + + public void stop() { + if(channel != null) { + channel.close(); // disconnects from channel and closes it + channel=null; + } + } + + + /** Pack the argument in a Info, serialize that one into the message buffer and send the message */ + public void sendNotification(Serializable n) { + sendNotification(null, n); + } + + /** Pack the argument in a Info, serialize that one into the message buffer and send the message */ + public void sendNotification(Address dest, Serializable n) { + Message msg=null; + byte[] data=null; + Info info; + + try { + if(n == null) return; + info=new Info(Info.NOTIFICATION, n); + data=Util.objectToByteBuffer(info); + msg=new Message(dest, null, data); + if(channel == null) { + if(log.isErrorEnabled()) log.error("channel is null. Won't send notification"); + return; + } + channel.send(msg); + } + catch(Throwable ex) { + if(log.isErrorEnabled()) log.error("error sending notification", ex); + } + } + + + /** + Determines the coordinator and asks it for its cache. If there is no coordinator (because we are first member), + null will be returned. Used only internally by NotificationBus. + @param timeout Max number of msecs until the call returns + @param max_tries Max number of attempts to fetch the cache from the coordinator + */ + public Serializable getCacheFromCoordinator(long timeout, int max_tries) { + return getCacheFromMember(null, timeout, max_tries); + } + + + /** + Determines the coordinator and asks it for its cache. If there is no coordinator (because we are first member), + null will be returned. Used only internally by NotificationBus. + @param mbr The address of the member from which to fetch the state. If null, the current coordinator + will be asked for the state + @param timeout Max number of msecs until the call returns - if timeout elapses + null will be returned + @param max_tries Max number of attempts to fetch the cache from the coordinator (will be set to 1 if < 1) + */ + public Serializable getCacheFromMember(Address mbr, long timeout, int max_tries) { + Serializable cache=null; + int num_tries=0; + Info info; + Message msg; + Address dst=mbr; // member from which to fetch the cache + + long start, stop; // +++ remove + + + if(max_tries < 1) max_tries=1; + + get_cache_promise.reset(); + while(num_tries <= max_tries) { + if(mbr == null) { // mbr == null means get cache from coordinator + dst=determineCoordinator(); + if(dst == null || dst.equals(getLocalAddress())) { // we are the first member --> empty cache + if(log.isInfoEnabled()) log.info("[" + getLocalAddress() + + "] no coordinator found --> first member (cache is empty)"); + return null; + } + } + + // +++ remove + if(log.isInfoEnabled()) log.info("[" + getLocalAddress() + "] dst=" + dst + + ", timeout=" + timeout + ", max_tries=" + max_tries + ", num_tries=" + num_tries); + + info=new Info(Info.GET_CACHE_REQ); + msg=new Message(dst, null, info); + try { + channel.send(msg); + } + catch(Exception e) { + log.error("failed sending message", e); + return null; + } + + start=System.currentTimeMillis(); + cache=get_cache_promise.getResult(timeout); + stop=System.currentTimeMillis(); + if(cache != null) { + if(log.isInfoEnabled()) log.info("got cache from " + + dst + ": cache is valid (waited " + (stop - start) + " msecs on get_cache_promise)"); + return cache; + } + else { + if(log.isErrorEnabled()) log.error("received null cache; retrying (waited " + + (stop - start) + " msecs on get_cache_promise)"); + } + + Util.sleep(500); + ++num_tries; + } + if(cache == null) + if(log.isErrorEnabled()) log.error("[" + getLocalAddress() + + "] cache is null (num_tries=" + num_tries + ')'); + return cache; + } + + + /** + Don't multicast this to all members, just apply it to local consumers. + */ + public void notifyConsumer(Serializable n) { + if(consumer != null && n != null) + consumer.handleNotification(n); + } + + + /* -------------------------------- Interface MessageListener -------------------------------- */ + public void receive(Message msg) { + Info info=null; + Object obj; + + if(msg == null || msg.getLength() == 0) return; + try { + obj=msg.getObject(); + if(!(obj instanceof Info)) { + if(log.isErrorEnabled()) log.error("expected an instance of Info (received " + + obj.getClass().getName() + ')'); + return; + } + info=(Info) obj; + switch(info.type) { + case Info.NOTIFICATION: + notifyConsumer(info.data); + break; + + case Info.GET_CACHE_REQ: + handleCacheRequest(msg.getSrc()); + break; + + case Info.GET_CACHE_RSP: + // +++ remove + if(log.isDebugEnabled()) log.debug("[GET_CACHE_RSP] cache was received from " + msg.getSrc()); + get_cache_promise.setResult(info.data); + break; + + default: + if(log.isErrorEnabled()) log.error("type " + info.type + " unknown"); + break; + } + } + catch(Throwable ex) { + if(log.isErrorEnabled()) log.error("exception=" + ex); + } + } + + public byte[] getState() { + return null; + } + + public void setState(byte[] state) { + } + + /* ----------------------------- End of Interface MessageListener ---------------------------- */ + + + + + /* ------------------------------- Interface MembershipListener ------------------------------ */ + + public synchronized void viewAccepted(View new_view) { + Vector joined_mbrs, left_mbrs, tmp; + Object tmp_mbr; + + if(new_view == null) return; + tmp=new_view.getMembers(); + + synchronized(members) { + // get new members + joined_mbrs=new Vector(); + for(int i=0; i < tmp.size(); i++) { + tmp_mbr=tmp.elementAt(i); + if(!members.contains(tmp_mbr)) + joined_mbrs.addElement(tmp_mbr); + } + + // get members that left + left_mbrs=new Vector(); + for(int i=0; i < members.size(); i++) { + tmp_mbr=members.elementAt(i); + if(!tmp.contains(tmp_mbr)) + left_mbrs.addElement(tmp_mbr); + } + + // adjust our own membership + members.removeAllElements(); + members.addAll(tmp); + } + + if(consumer != null) { + if(!joined_mbrs.isEmpty()) + for(int i=0; i < joined_mbrs.size(); i++) + consumer.memberJoined((Address) joined_mbrs.elementAt(i)); + if(!left_mbrs.isEmpty()) + for(int i=0; i < left_mbrs.size(); i++) + consumer.memberLeft((Address) left_mbrs.elementAt(i)); + } + } + + + public void suspect(Address suspected_mbr) { + } + + public void block() { + } + + + /* ----------------------------- End of Interface MembershipListener ------------------------- */ + + + + + + + + /* ------------------------------------- Private Methods ------------------------------------- */ + + Address determineCoordinator() { + Vector v=channel != null ? channel.getView().getMembers() : null; + return v != null ? (Address) v.elementAt(0) : null; + } + + + void handleCacheRequest(Address sender) throws ChannelException { + Serializable cache=null; + Message msg; + Info info; + + if(sender == null) { + // +++ remove + // + if(log.isErrorEnabled()) log.error("sender is null"); + return; + } + + synchronized(cache_mutex) { + cache=getCache(); // get the cache from the consumer + info=new Info(Info.GET_CACHE_RSP, cache); + msg=new Message(sender, null, info); + if(log.isInfoEnabled()) log.info("[" + getLocalAddress() + "] returning cache to " + sender); + channel.send(msg); + } + } + + public Serializable getCache() { + return consumer != null ? consumer.getCache() : null; + } + + + + /* --------------------------------- End of Private Methods ---------------------------------- */ + + + + + + private static class Info implements Serializable { + public final static int NOTIFICATION=1; + public final static int GET_CACHE_REQ=2; + public final static int GET_CACHE_RSP=3; + + + int type=0; + Serializable data=null; // if type == NOTIFICATION data is notification, if type == GET_CACHE_RSP, data is cache + private static final long serialVersionUID = -7198723001828406107L; + + + public Info(int type) { + this.type=type; + } + + public Info(int type, Serializable data) { + this.type=type; + this.data=data; + } + + + public String toString() { + StringBuilder sb=new StringBuilder(); + sb.append("type= "); + if(type == NOTIFICATION) + sb.append("NOTIFICATION"); + else if(type == GET_CACHE_REQ) + sb.append("GET_CACHE_REQ"); + else if(type == GET_CACHE_RSP) + sb.append("GET_CACHE_RSP"); + else + sb.append(""); + if(data != null) { + if(type == NOTIFICATION) + sb.append(", notification=" + data); + else if(type == GET_CACHE_RSP) sb.append(", cache=" + data); + } + return sb.toString(); + } + } + + +} + + + Index: 3rdParty_sources/jgroups/org/jgroups/blocks/PullPushAdapter.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/blocks/PullPushAdapter.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/blocks/PullPushAdapter.java 17 Aug 2012 14:51:19 -0000 1.1 @@ -0,0 +1,480 @@ +// $Id: PullPushAdapter.java,v 1.1 2012/08/17 14:51:19 marcin Exp $ + +package org.jgroups.blocks; + + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.*; +import org.jgroups.util.Util; + +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; + + +/** + * Allows a client of {@link org.jgroups.Channel} to be notified when messages have been received + * instead of having to actively poll the channel for new messages. Typically used in the + * client role (receive()). As this class does not implement interface + * {@link org.jgroups.Transport}, but uses it for receiving messages, an underlying object + * has to be used to send messages (e.g. the channel on which an object of this class relies).

+ * Multiple MembershipListeners can register with the PullPushAdapter; when a view is received, they + * will all be notified. There is one main message listener which sends and receives message. In addition, + * MessageListeners can register with a certain tag (identifier), and then send messages tagged with this + * identifier. When a message with such an identifier is received, the corresponding MessageListener will be + * looked up and the message dispatched to it. If no tag is found (default), the main MessageListener will + * receive the message. + * @author Bela Ban + * @version $Revision + * @deprecated Use {@link org.jgroups.Receiver} instead, this class will be removed in JGroups 3.0 + */ +public class PullPushAdapter implements Runnable, ChannelListener { + protected Transport transport=null; + protected MessageListener listener=null; // main message receiver + protected final List membership_listeners=new ArrayList(); + protected Thread receiver_thread=null; + protected final HashMap listeners=new HashMap(); // keys=identifier (Serializable), values=MessageListeners + protected final Log log=LogFactory.getLog(getClass()); + static final String PULL_HEADER="PULL_HEADER"; + + + public PullPushAdapter(Transport transport) { + this.transport=transport; + start(); + } + + public PullPushAdapter(Transport transport, MessageListener l) { + this.transport=transport; + setListener(l); + start(); + } + + + public PullPushAdapter(Transport transport, MembershipListener ml) { + this.transport=transport; + addMembershipListener(ml); + start(); + } + + + public PullPushAdapter(Transport transport, MessageListener l, MembershipListener ml) { + this.transport=transport; + setListener(l); + addMembershipListener(ml); + start(); + } + + + public PullPushAdapter(Transport transport, MessageListener l, MembershipListener ml, boolean start) { + this.transport=transport; + setListener(l); + addMembershipListener(ml); + if(start) + start(); + } + + + + public Transport getTransport() { + return transport; + } + + + public final void start() { + if(receiver_thread == null || !receiver_thread.isAlive()) { + receiver_thread=new Thread(this, "PullPushAdapterThread"); + receiver_thread.setDaemon(true); + receiver_thread.start(); + } + if(transport instanceof JChannel) + ((JChannel)transport).addChannelListener(this); + } + + public void stop() { + Thread tmp=null; + if(receiver_thread != null && receiver_thread.isAlive()) { + tmp=receiver_thread; + receiver_thread=null; + tmp.interrupt(); + try { + tmp.join(1000); + } + catch(Exception ex) { + } + } + receiver_thread=null; + } + + /** + * Sends a message to the group - listeners to this identifier will receive the messages. + * @param identifier the key that the proper listeners are listenting on + * @param msg the Message to be sent + * @see #registerListener + */ + public void send(Serializable identifier, Message msg) throws Exception { + if(msg == null) { + if(log.isErrorEnabled()) log.error("msg is null"); + return; + } + if(identifier == null) + transport.send(msg); + else { + msg.putHeader(PULL_HEADER, new PullHeader(identifier)); + transport.send(msg); + } + } + + /** + * Sends a message with no identifier; listener member will get this message on the other group members. + * @param msg the Message to be sent + * @throws Exception + */ + public void send(Message msg) throws Exception { + send(null, msg); + } + + + public final void setListener(MessageListener l) { + listener=l; + } + + + + /** + * Sets a listener to messages with a given identifier. + * Messages sent with this identifier in their headers will be routed to this listener. + * Note: there can be only one listener for one identifier; + * if you want to register a different listener to an already registered identifier, then unregister first. + * @param identifier - messages sent on the group with this object will be received by this listener + * @param l - the listener that will get the message + */ + public void registerListener(Serializable identifier, MessageListener l) { + if(l == null || identifier == null) { + if(log.isErrorEnabled()) log.error("message listener or identifier is null"); + return; + } + if(listeners.containsKey(identifier)) { + if(log.isErrorEnabled()) log.error("listener with identifier=" + identifier + + " already exists, choose a different identifier or unregister current listener"); + // we do not want to overwrite the listener + return; + } + listeners.put(identifier, l); + } + + /** + * Removes a message listener to a given identifier from the message listeners map. + * @param identifier - the key to whom we do not want to listen any more + */ + public void unregisterListener(Serializable identifier) { + listeners.remove(identifier); + } + + + /** @deprecated Use {@link #addMembershipListener} */ + public void setMembershipListener(MembershipListener ml) { + addMembershipListener(ml); + } + + public final void addMembershipListener(MembershipListener l) { + if(l != null && !membership_listeners.contains(l)) + membership_listeners.add(l); + } + + public void removeMembershipListener(MembershipListener l) { + if(l != null && membership_listeners.contains(l)) + membership_listeners.remove(l); + } + + + /** + * Reentrant run(): message reception is serialized, then the listener is notified of the + * message reception + */ + public void run() { + Object obj; + + while(receiver_thread != null && Thread.currentThread().equals(receiver_thread)) { + try { + obj=transport.receive(0); + if(obj == null) + continue; + + if(obj instanceof Message) { + handleMessage((Message)obj); + } + else if(obj instanceof GetStateEvent) { + byte[] retval=null; + GetStateEvent evt=(GetStateEvent)obj; + String state_id=evt.getStateId(); + if(listener != null) { + try { + if(listener instanceof ExtendedMessageListener && state_id!=null) { + retval=((ExtendedMessageListener)listener).getState(state_id); + } + else { + retval=listener.getState(); + } + } + catch(Throwable t) { + log.error("getState() from application failed, will return empty state", t); + } + } + else { + log.warn("no listener registered, returning empty state"); + } + + if(transport instanceof Channel) { + ((Channel)transport).returnState(retval, state_id); + } + else { + if(log.isErrorEnabled()) + log.error("underlying transport is not a Channel, but a " + + transport.getClass().getName() + ": cannot return state using returnState()"); + } + } + else if(obj instanceof SetStateEvent) { + SetStateEvent evt=(SetStateEvent)obj; + String state_id=evt.getStateId(); + if(listener != null) { + try { + if(listener instanceof ExtendedMessageListener && state_id!=null) { + ((ExtendedMessageListener)listener).setState(state_id, evt.getArg()); + } + else { + listener.setState(evt.getArg()); + } + } + catch(ClassCastException cast_ex) { + if(log.isErrorEnabled()) log.error("received SetStateEvent, but argument " + + ((SetStateEvent)obj).getArg() + " is not serializable ! Discarding message."); + } + } + } + else if(obj instanceof StreamingGetStateEvent) { + StreamingGetStateEvent evt=(StreamingGetStateEvent)obj; + if(listener instanceof ExtendedMessageListener) + { + if(evt.getStateId()==null) + { + ((ExtendedMessageListener)listener).getState(evt.getArg()); + } + else + { + ((ExtendedMessageListener)listener).getState(evt.getStateId(),evt.getArg()); + } + } + } + else if(obj instanceof StreamingSetStateEvent) { + StreamingSetStateEvent evt=(StreamingSetStateEvent)obj; + if(listener instanceof ExtendedMessageListener) + { + if(evt.getStateId()==null) + { + ((ExtendedMessageListener)listener).setState(evt.getArg()); + } + else + { + ((ExtendedMessageListener)listener).setState(evt.getStateId(),evt.getArg()); + } + } + } + else if(obj instanceof View) { + notifyViewChange((View)obj); + } + else if(obj instanceof SuspectEvent) { + notifySuspect((Address)((SuspectEvent)obj).getMember()); + } + else if(obj instanceof BlockEvent) { + notifyBlock(); + if(transport instanceof Channel) { + ((Channel)transport).blockOk(); + } + } + else if(obj instanceof UnblockEvent) { + notifyUnblock(); + } + } + catch(ChannelNotConnectedException conn) { + Address local_addr=((Channel)transport).getLocalAddress(); + if(log.isTraceEnabled()) log.trace('[' + (local_addr == null ? "" : local_addr.toString()) + + "] channel not connected, exception is " + conn); + Util.sleep(1000); + receiver_thread=null; + break; + } + catch(ChannelClosedException closed_ex) { + Address local_addr=((Channel)transport).getLocalAddress(); + if(log.isTraceEnabled()) log.trace('[' + (local_addr == null ? "" : local_addr.toString()) + + "] channel closed, exception is " + closed_ex); + // Util.sleep(1000); + receiver_thread=null; + break; + } + catch(Throwable e) { + } + } + } + + + /** + * Check whether the message has an identifier. If yes, lookup the MessageListener associated with the + * given identifier in the hashtable and dispatch to it. Otherwise just use the main (default) message + * listener + */ + protected void handleMessage(Message msg) { + PullHeader hdr=(PullHeader)msg.getHeader(PULL_HEADER); + Serializable identifier; + MessageListener l; + + if(hdr != null && (identifier=hdr.getIdentifier()) != null) { + l=(MessageListener)listeners.get(identifier); + if(l == null) { + if(log.isErrorEnabled()) log.error("received a messages tagged with identifier=" + + identifier + ", but there is no registration for that identifier. Will drop message"); + } + else + l.receive(msg); + } + else { + if(listener != null) + listener.receive(msg); + } + } + + + protected void notifyViewChange(View v) { + MembershipListener l; + + if(v == null) return; + for(Iterator it=membership_listeners.iterator(); it.hasNext();) { + l=(MembershipListener)it.next(); + try { + l.viewAccepted(v); + } + catch(Throwable ex) { + if(log.isErrorEnabled()) log.error("exception notifying " + l + " of view(" + v + ")", ex); + } + } + } + + protected void notifySuspect(Address suspected_mbr) { + MembershipListener l; + + if(suspected_mbr == null) return; + for(Iterator it=membership_listeners.iterator(); it.hasNext();) { + l=(MembershipListener)it.next(); + try { + l.suspect(suspected_mbr); + } + catch(Throwable ex) { + if(log.isErrorEnabled()) log.error("exception notifying " + l + " of suspect(" + suspected_mbr + ")", ex); + } + } + } + + protected void notifyBlock() { + MembershipListener l; + + for(Iterator it=membership_listeners.iterator(); it.hasNext();) { + l=(MembershipListener)it.next(); + try { + l.block(); + } + catch(Throwable ex) { + if(log.isErrorEnabled()) log.error("exception notifying " + l + " of block()", ex); + } + } + } + + protected void notifyUnblock() { + MembershipListener l; + + for(Iterator it=membership_listeners.iterator(); it.hasNext();) { + l=(MembershipListener)it.next(); + if(l instanceof ExtendedMembershipListener){ + try { + ((ExtendedMembershipListener)l).unblock(); + } + catch(Throwable ex) { + if(log.isErrorEnabled()) log.error("exception notifying " + l + " of unblock()", ex); + } + } + } + } + + public void channelConnected(Channel channel) { + if(log.isTraceEnabled()) + log.trace("channel is connected"); + } + + public void channelDisconnected(Channel channel) { + if(log.isTraceEnabled()) + log.trace("channel is disconnected"); + } + + public void channelClosed(Channel channel) { + } + + public void channelShunned() { + if(log.isTraceEnabled()) + log.trace("channel is shunned"); + } + + public void channelReconnected(Address addr) { + start(); + } + + + + + public static final class PullHeader extends Header { + Serializable identifier=null; + + public PullHeader() { + ; // used by externalization + } + + public PullHeader(Serializable identifier) { + this.identifier=identifier; + } + + public Serializable getIdentifier() { + return identifier; + } + + public int size() { + if(identifier == null) + return 12; + else + return 64; + } + + + public String toString() { + return "PullHeader"; + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeObject(identifier); + } + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + identifier=(Serializable)in.readObject(); + } + } + + + /** + * @return Returns the listener. + */ + public MessageListener getListener() { + return listener; + } +} Index: 3rdParty_sources/jgroups/org/jgroups/blocks/ReplicatedHashMap.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/blocks/ReplicatedHashMap.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/blocks/ReplicatedHashMap.java 17 Aug 2012 14:51:18 -0000 1.1 @@ -0,0 +1,1043 @@ +package org.jgroups.blocks; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.*; +import org.jgroups.persistence.PersistenceFactory; +import org.jgroups.persistence.PersistenceManager; +import org.jgroups.util.Promise; +import org.jgroups.util.Util; + +import java.io.*; +import java.lang.reflect.Method; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArraySet; + + +/** + * Subclass of a {@link java.util.concurrent.ConcurrentHashMap} with replication of the contents across a cluster. + * Any change to the hashmap (clear(), put(), remove() etc) will transparently be + * propagated to all replicas in the group. All read-only methods will always access the local replica.

+ * Keys and values added to the hashmap must be serializable, the reason + * being that they will be sent across the network to all replicas of the group. Having said + * this, it is now for example possible to add RMI remote objects to the hashtable as they + * are derived from java.rmi.server.RemoteObject which in turn is serializable. + * This allows to lookup shared distributed objects by their name and invoke methods on them, + * regardless of one's onw location. A ReplicatedHashMap thus allows to + * implement a distributed naming service in just a couple of lines.

+ * An instance of this class will contact an existing member of the group to fetch its + * initial state.

+ * This class combines both {@link org.jgroups.blocks.ReplicatedHashtable} (asynchronous replication) and + * {@link org.jgroups.blocks.DistributedHashtable} (synchronous replication) into one class + * @author Bela Ban + * @version $Id: ReplicatedHashMap.java,v 1.1 2012/08/17 14:51:18 marcin Exp $ + */ +public class ReplicatedHashMap extends ConcurrentHashMap implements ExtendedReceiver, ReplicatedMap { + private static final long serialVersionUID=-5317720987340048547L; + + + public interface Notification { + void entrySet(K key, V value); + + void entryRemoved(K key); + + void viewChange(View view, Vector

new_mbrs, Vector
old_mbrs); + + void contentsSet(Map new_entries); + + void contentsCleared(); + } + + private static final short PUT = 1; + private static final short PUT_IF_ABSENT = 2; + private static final short PUT_ALL = 3; + private static final short REMOVE = 4; + private static final short REMOVE_IF_EQUALS = 5; + private static final short REPLACE_IF_EXISTS = 6; + private static final short REPLACE_IF_EQUALS = 7; + private static final short CLEAR = 8; + + + protected static Map methods; + + static { + try { + methods=new HashMap(8); + methods.put(PUT, ReplicatedHashMap.class.getMethod("_put", Serializable.class, Serializable.class)); + methods.put(PUT_IF_ABSENT, ReplicatedHashMap.class.getMethod("_putIfAbsent", Serializable.class, Serializable.class)); + methods.put(PUT_ALL, ReplicatedHashMap.class.getMethod("_putAll", Map.class)); + methods.put(REMOVE, ReplicatedHashMap.class.getMethod("_remove", Object.class)); + methods.put(REMOVE_IF_EQUALS, ReplicatedHashMap.class.getMethod("_remove", Object.class, Object.class)); + methods.put(REPLACE_IF_EXISTS, ReplicatedHashMap.class.getMethod("_replace", Serializable.class, Serializable.class)); + methods.put(REPLACE_IF_EQUALS, ReplicatedHashMap.class.getMethod("_replace", Serializable.class, Serializable.class, Serializable.class)); + methods.put(CLEAR, ReplicatedHashMap.class.getMethod("_clear")); + } + catch(NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + private transient Channel channel; + protected transient RpcDispatcher disp=null; + private String cluster_name=null; + // to be notified when mbrship changes + private final transient Set notifs=new CopyOnWriteArraySet(); + private final Vector
members=new Vector
(); // keeps track of all DHTs + private transient boolean persistent=false; // whether to use PersistenceManager to save state + private transient PersistenceManager persistence_mgr=null; + + /** + * Determines when the updates have to be sent across the network, avoids sending unnecessary + * messages when there are no member in the group + */ + private transient boolean send_message=false; + + protected final transient Promise state_promise=new Promise(); + + /** + * Whether updates across the cluster should be asynchronous (default) or synchronous) + */ + protected int update_mode=GroupRequest.GET_NONE; + + /** + * For blocking updates only: the max time to wait (0 == forever) + */ + protected long timeout=5000; + + protected final Log log=LogFactory.getLog(this.getClass()); + + + /** + * Creates a ReplicatedHashMap + * @param clustername The name of the group to join + * @param factory The ChannelFactory which will be used to create a channel + * @param properties The property string to be used to define the channel. This will override the properties of + * the factory. If null, then the factory properties will be used + * @param state_timeout The time to wait until state is retrieved in milliseconds. A value of 0 means wait forever. + */ + public ReplicatedHashMap(String clustername, ChannelFactory factory, String properties, long state_timeout) + throws ChannelException { + this.cluster_name=clustername; + if(factory != null) { + channel=properties != null? factory.createChannel((Object)properties) : factory.createChannel(); + } + else { + channel=new JChannel(properties); + } + disp=new RpcDispatcher(channel, this, this, this); + disp.setMethodLookup(new MethodLookup() { + public Method findMethod(short id) { + return methods.get(id); + } + }); + channel.connect(clustername); + start(state_timeout); + } + + /** + * Creates a ReplicatedHashMap. Optionally the contents can be saved to + * persistemt storage using the {@link org.jgroups.persistence.PersistenceManager}. + * @param clustername Name of the group to join + * @param factory Instance of a ChannelFactory to create the channel + * @param properties Protocol stack properties. This will override the properties of the factory. If + * null, then the factory properties will be used + * @param persistent Whether the contents should be persisted + * @param state_timeout Max number of milliseconds to wait until the state is retrieved + */ + public ReplicatedHashMap(String clustername, ChannelFactory factory, String properties, + boolean persistent, long state_timeout) + throws ChannelException { + this.cluster_name=clustername; + this.persistent=persistent; + if(factory != null) { + channel=properties != null? factory.createChannel((Object)properties) : factory.createChannel(); + } + else { + channel=new JChannel(properties); + } + disp=new RpcDispatcher(channel, this, this, this); + disp.setMethodLookup(new MethodLookup() { + public Method findMethod(short id) { + return methods.get(id); + } + }); + channel.connect(clustername); + start(state_timeout); + } + + + public ReplicatedHashMap(Channel channel) { + this(channel, false); + } + + + public ReplicatedHashMap(Channel channel, boolean persistent) { + this.cluster_name=channel.getClusterName(); + this.channel=channel; + this.persistent=persistent; + init(); + } + + + protected final void init() { + disp=new RpcDispatcher(channel, this, this, this); + disp.setMethodLookup(new MethodLookup() { + public Method findMethod(short id) { + return methods.get(id); + } + }); + + // Changed by bela (jan 20 2003): start() has to be called by user (only when providing + // own channel). First, Channel.connect() has to be called, then start(). + // start(state_timeout); + } + + + public boolean isBlockingUpdates() { + return update_mode == GroupRequest.GET_ALL; + } + + /** + * Whether updates across the cluster should be asynchronous (default) or synchronous) + * @param blocking_updates + */ + public void setBlockingUpdates(boolean blocking_updates) { + this.update_mode=blocking_updates? GroupRequest.GET_ALL : GroupRequest.GET_NONE; + } + + /** + * The timeout (in milliseconds) for blocking updates + */ + public long getTimeout() { + return timeout; + } + + /** + * Sets the cluster call timeout (until all acks have been received) + * @param timeout The timeout (in milliseconds) for blocking updates + */ + public void setTimeout(long timeout) { + this.timeout=timeout; + } + + /** + * Fetches the state + * @param state_timeout + * @throws org.jgroups.ChannelClosedException + * @throws org.jgroups.ChannelNotConnectedException + * + */ + public final void start(long state_timeout) throws ChannelClosedException, ChannelNotConnectedException { + boolean rc; + if(persistent) { + if(log.isInfoEnabled()) log.info("fetching state from database"); + try { + persistence_mgr=PersistenceFactory.getInstance().createManager(); + } + catch(Throwable ex) { + if(log.isErrorEnabled()) log.error("failed creating PersistenceManager, " + + "turning persistency off. Exception: " + Util.printStackTrace(ex)); + persistent=false; + } + } + + state_promise.reset(); + rc=channel.getState(null, state_timeout); + if(rc) { + if(log.isInfoEnabled()) log.info("state was retrieved successfully, waiting for setState()"); + Boolean result=state_promise.getResult(state_timeout); + if(result == null) { + if(log.isErrorEnabled()) log.error("setState() never got called"); + } + else { + if(log.isInfoEnabled()) log.info("setState() was called"); + } + } + else { + if(log.isInfoEnabled()) log.info("state could not be retrieved (first member)"); + if(persistent) { + if(log.isInfoEnabled()) log.info("fetching state from database"); + try { + Map m=persistence_mgr.retrieveAll(); + if(m != null) { + K key; + V val; + for(Map.Entry entry: m.entrySet()) { + key=entry.getKey(); + val=entry.getValue(); + if(log.isTraceEnabled()) log.trace("inserting " + key + " --> " + val); + put(key, val); // will replicate key and value + } + } + } + catch(Throwable ex) { + if(log.isErrorEnabled()) log.error("failed creating PersistenceManager, " + + "turning persistency off. Exception: " + Util.printStackTrace(ex)); + persistent=false; + } + } + } + } + + + public Address getLocalAddress() { + return channel != null? channel.getLocalAddress() : null; + } + + public String getClusterName() { + return cluster_name; + } + + public Channel getChannel() { + return channel; + } + + public boolean getPersistent() { + return persistent; + } + + public void setPersistent(boolean p) { + persistent=p; + } + + + public void setDeadlockDetection(boolean flag) { + if(disp != null) + disp.setDeadlockDetection(flag); + } + + public void addNotifier(Notification n) { + if(n != null) + notifs.add(n); + } + + public void removeNotifier(Notification n) { + if(n != null) + notifs.remove(n); + } + + public void stop() { + if(disp != null) { + disp.stop(); + disp=null; + } + if(channel != null) { + channel.close(); + channel=null; + } + } + + + /** + * Maps the specified key to the specified value in this table. + * Neither the key nor the value can be null. + *

+ *

The value can be retrieved by calling the get method + * with a key that is equal to the original key. + * @param key key with which the specified value is to be associated + * @param value value to be associated with the specified key + * @return the previous value associated with key, or + * null if there was no mapping for key + * @throws NullPointerException if the specified key or value is null + */ + public V put(K key, V value) { + V prev_val=get(key); + + if(send_message == true) { + try { + MethodCall call=new MethodCall(PUT, new Object[]{key, value}); + disp.callRemoteMethods(null, call, update_mode, timeout); + } + catch(Exception e) { + throw new RuntimeException("put(" + key + ", " + value + ") failed", e); + } + } + else { + return _put(key, value); + } + return prev_val; + } + + + + /** + * {@inheritDoc} + * @return the previous value associated with the specified key, + * or null if there was no mapping for the key + * @throws NullPointerException if the specified key or value is null + */ + public V putIfAbsent(K key, V value) { + V prev_val=get(key); + + if(send_message == true) { + try { + MethodCall call=new MethodCall(PUT_IF_ABSENT, new Object[]{key, value}); + disp.callRemoteMethods(null, call, update_mode, timeout); + } + catch(Exception e) { + throw new RuntimeException("putIfAbsent(" + key + ", " + value + ") failed", e); + } + } + else { + return _putIfAbsent(key, value); + } + return prev_val; + } + + + /** + * Copies all of the mappings from the specified map to this one. + * These mappings replace any mappings that this map had for any of the + * keys currently in the specified map. + * @param m mappings to be stored in this map + */ + public void putAll(Map m) { + if(send_message == true) { + try { + MethodCall call=new MethodCall(PUT_ALL, new Object[]{m}); + disp.callRemoteMethods(null, call, update_mode, timeout); + } + catch(Throwable t) { + throw new RuntimeException("putAll() failed", t); + } + } + else { + _putAll(m); + } + } + + /** + * Removes all of the mappings from this map. + */ + public void clear() { + //Changes done by + //if true, propagate action to the group + if(send_message == true) { + try { + MethodCall call=new MethodCall(CLEAR, null); + disp.callRemoteMethods(null, call, update_mode, timeout); + } + catch(Exception e) { + throw new RuntimeException("clear() failed", e); + } + } + else { + _clear(); + } + } + + /** + * Removes the key (and its corresponding value) from this map. + * This method does nothing if the key is not in the map. + * @param key the key that needs to be removed + * @return the previous value associated with key, or + * null if there was no mapping for key + * @throws NullPointerException if the specified key is null + */ + public V remove(Object key) { + V retval=get(key); + + if(send_message == true) { + try { + MethodCall call=new MethodCall(REMOVE, new Object[]{key}); + disp.callRemoteMethods(null, call, update_mode, timeout); + } + catch(Exception e) { + throw new RuntimeException("remove(" + key + ") failed", e); + } + } + else { + return _remove(key); + } + return retval; + } + + /** + * {@inheritDoc} + * @throws NullPointerException if the specified key is null + */ + public boolean remove(Object key, Object value) { + Object val=get(key); + boolean removed=val != null && value != null && val.equals(value); + + if(send_message == true) { + try { + MethodCall call=new MethodCall(REMOVE_IF_EQUALS, new Object[]{key, value}); + disp.callRemoteMethods(null, call, update_mode, timeout); + } + catch(Exception e) { + throw new RuntimeException("remove(" + key + ", " + value + ") failed", e); + } + } + else { + return _remove(key, value); + } + return removed; + } + + + /** + * {@inheritDoc} + * @throws NullPointerException if any of the arguments are null + */ + public boolean replace(K key, V oldValue, V newValue) { + Object val=get(key); + boolean replaced=val != null && oldValue != null && val.equals(oldValue); + + if(send_message == true) { + try { + MethodCall call=new MethodCall(REPLACE_IF_EQUALS, new Object[]{key, oldValue, newValue}); + disp.callRemoteMethods(null, call, update_mode, timeout); + } + catch(Exception e) { + throw new RuntimeException("replace(" + key + ", " + oldValue + ", " + newValue + ") failed", e); + } + } + else { + return _replace(key, oldValue,newValue); + } + return replaced; + } + + /** + * {@inheritDoc} + * @return the previous value associated with the specified key, + * or null if there was no mapping for the key + * @throws NullPointerException if the specified key or value is null + */ + public V replace(K key, V value) { + V retval=get(key); + + if(send_message == true) { + try { + MethodCall call=new MethodCall(REPLACE_IF_EXISTS, new Object[]{key, value}); + disp.callRemoteMethods(null, call, update_mode, timeout); + } + catch(Exception e) { + throw new RuntimeException("replace(" + key + ", " + value + ") failed", e); + } + } + else { + return _replace(key, value); + } + return retval; + } + + /*------------------------ Callbacks -----------------------*/ + + public V _put(K key, V value) { + V retval=super.put(key, value); + if(persistent) { + try { + persistence_mgr.save(key, value); + } + catch(Throwable t) { + if(log.isErrorEnabled()) + log.error("failed persisting " + key + " + " + value, t); + } + } + for(Notification notif: notifs) + notif.entrySet(key, value); + return retval; + } + + public V _putIfAbsent(K key, V value) { + V retval=super.putIfAbsent(key, value); + if(persistent) { + try { + persistence_mgr.save(key, value); + } + catch(Throwable t) { + if(log.isErrorEnabled()) + log.error("failed persisting " + key + " + " + value, t); + } + } + for(Notification notif: notifs) + notif.entrySet(key, value); + return retval; + } + + + /** + * @see java.util.Map#putAll(java.util.Map) + */ + public void _putAll(Map map) { + if(map == null) + return; + + // Calling the method below seems okay, but would result in ... deadlock ! + // The reason is that Map.putAll() calls put(), which we override, which results in + // lock contention for the map. + + // ---> super.putAll(m); <--- CULPRIT !!!@#$%$ + + // That said let's do it the stupid way: + for(Map.Entry entry: map.entrySet()) { + super.put(entry.getKey(), entry.getValue()); + } + + if(persistent && !map.isEmpty()) { + try { + persistence_mgr.saveAll(map); + } + catch(Throwable t) { + if(log.isErrorEnabled()) log.error("failed persisting contents", t); + } + } + if(!map.isEmpty()) { + for(Notification notif: notifs) + notif.contentsSet(map); + } + } + + + public void _clear() { + super.clear(); + if(persistent) { + try { + persistence_mgr.clear(); + } + catch(Throwable t) { + if(log.isErrorEnabled()) log.error("failed clearing contents", t); + } + } + for(Notification notif: notifs) + notif.contentsCleared(); + } + + + public V _remove(Object key) { + V retval=super.remove(key); + if(persistent && retval != null) { + try { + persistence_mgr.remove((Serializable)key); + } + catch(Throwable t) { + if(log.isErrorEnabled()) log.error("failed removing " + key, t); + } + } + if(retval != null) { + for(Notification notif: notifs) + notif.entryRemoved((K)key); + } + + return retval; + } + + + + public boolean _remove(Object key, Object value) { + boolean removed=super.remove(key, value); + if(persistent && removed) { + try { + persistence_mgr.remove((Serializable)key); + } + catch(Throwable t) { + if(log.isErrorEnabled()) log.error("failed removing " + key, t); + } + } + if(removed) { + for(Notification notif: notifs) + notif.entryRemoved((K)key); + } + return removed; + } + + public boolean _replace(K key, V oldValue, V newValue) { + boolean replaced=super.replace(key, oldValue, newValue); + if(persistent && replaced) { + try { + persistence_mgr.save(key, newValue); + } + catch(Throwable t) { + if(log.isErrorEnabled()) log.error("failed saving " + key + ", " + newValue, t); + } + } + if(replaced) { + for(Notification notif: notifs) + notif.entrySet(key, newValue); + } + return replaced; + } + + public V _replace(K key, V value) { + V retval=super.replace(key, value); + if(persistent) { + try { + persistence_mgr.save(key, value); + } + catch(Throwable t) { + if(log.isErrorEnabled()) log.error("failed saving " + key + ", " + value, t); + } + } + for(Notification notif: notifs) + notif.entrySet(key, value); + return retval; + } + + /*----------------------------------------------------------*/ + + /*-------------------- State Exchange ----------------------*/ + + public void receive(Message msg) { + } + + public byte[] getState() { + K key; + V val; + Map copy=new HashMap(); + + for(Map.Entry entry: entrySet()) { + key=entry.getKey(); + val=entry.getValue(); + copy.put(key, val); + } + try { + return Util.objectToByteBuffer(copy); + } + catch(Throwable ex) { + if(log.isErrorEnabled()) log.error("exception marshalling state: " + ex); + return null; + } + } + + + public void setState(byte[] new_state) { + HashMap new_copy; + + try { + new_copy=(HashMap)Util.objectFromByteBuffer(new_state); + if(new_copy == null) + return; + } + catch(Throwable ex) { + if(log.isErrorEnabled()) log.error("exception unmarshalling state: " + ex); + return; + } + _putAll(new_copy); + state_promise.setResult(Boolean.TRUE); + } + + /*------------------- Membership Changes ----------------------*/ + + public void viewAccepted(View new_view) { + Vector

new_mbrs=new_view.getMembers(); + + if(new_mbrs != null) { + sendViewChangeNotifications(new_view, new_mbrs, new Vector
(members)); // notifies observers (joined, left) + members.clear(); + members.addAll(new_mbrs); + } + //if size is bigger than one, there are more peers in the group + //otherwise there is only one server. + send_message=members.size() > 1; + } + + + /** + * Called when a member is suspected + */ + public void suspect(Address suspected_mbr) { + ; + } + + + /** + * Block sending and receiving of messages until ViewAccepted is called + */ + public void block() { + } + + + void sendViewChangeNotifications(View view, Vector
new_mbrs, Vector
old_mbrs) { + Vector
joined, left; + + if((notifs.isEmpty()) || (old_mbrs == null) || (new_mbrs == null) || + (old_mbrs.isEmpty()) || (new_mbrs.isEmpty())) + return; + + // 1. Compute set of members that joined: all that are in new_mbrs, but not in old_mbrs + joined=new Vector
(); + for(Address mbr: new_mbrs) { + if(!old_mbrs.contains(mbr)) + joined.addElement(mbr); + } + + // 2. Compute set of members that left: all that were in old_mbrs, but not in new_mbrs + left=new Vector
(); + for(Address mbr: old_mbrs) { + if(!new_mbrs.contains(mbr)) { + left.addElement(mbr); + } + } + + for(Notification notif: notifs) { + notif.viewChange(view, joined, left); + } + } + + + + + public byte[] getState(String state_id) { + // not implemented + return null; + } + + public void getState(OutputStream ostream) { + K key; + V val; + HashMap copy=new HashMap(); + ObjectOutputStream oos=null; + + for(Map.Entry entry: entrySet()) { + key=entry.getKey(); + val=entry.getValue(); + copy.put(key, val); + } + try { + oos=new ObjectOutputStream(ostream); + oos.writeObject(copy); + } + catch(Throwable ex) { + if(log.isErrorEnabled()) log.error("exception marshalling state: " + ex); + } + finally { + Util.close(oos); + } + } + + public void getState(String state_id, OutputStream ostream) { + } + + public void setState(String state_id, byte[] state) { + } + + public void setState(InputStream istream) { + HashMap new_copy=null; + ObjectInputStream ois=null; + try { + ois=new ObjectInputStream(istream); + new_copy=(HashMap)ois.readObject(); + ois.close(); + } + catch(Throwable e) { + e.printStackTrace(); + if(log.isErrorEnabled()) log.error("exception marshalling state: " + e); + } + finally { + Util.close(ois); + } + if(new_copy != null) + _putAll(new_copy); + + state_promise.setResult(Boolean.TRUE); + } + + public void setState(String state_id, InputStream istream) { + } + + public void unblock() { + } + + + /** + * Creates a synchronized facade for a ReplicatedMap. All methods which change state are invoked through a monitor. + * This is similar to {@Collections.synchronizedMap()}, but also includes the replication methods (starting + * with an underscore). + * @param map + * @return + */ + public static ReplicatedMap synchronizedMap(ReplicatedMap map) { + return new SynchronizedReplicatedMap(map); + } + + private static class SynchronizedReplicatedMap implements ReplicatedMap { + private final ReplicatedMap map; + private final Object mutex; + + + private SynchronizedReplicatedMap(ReplicatedMap map) { + this.map=map; + this.mutex=this; + } + + public int size() { + synchronized(mutex) { + return map.size(); + } + } + + public boolean isEmpty() { + synchronized(mutex) { + return map.isEmpty(); + } + } + + public boolean containsKey(Object key) { + synchronized(mutex) { + return map.containsKey(key); + } + } + + public boolean containsValue(Object value) { + synchronized(mutex) { + return map.containsValue(value); + } + } + + public V get(Object key) { + synchronized(mutex) { + return map.get(key); + } + } + + public V put(K key, V value) { + synchronized(mutex) { + return map.put(key, value); + } + } + + public void putAll(Map m) { + synchronized(mutex) { + map.putAll(m); + } + } + + public void clear() { + synchronized(mutex) { + map.clear(); + } + } + + public V putIfAbsent(K key, V value) { + synchronized(mutex) { + return map.putIfAbsent(key, value); + } + } + + public boolean remove(Object key, Object value) { + synchronized(mutex) { + return map.remove(key, value); + } + } + + public boolean replace(K key, V oldValue, V newValue) { + synchronized(mutex) { + return map.replace(key, oldValue, newValue); + } + } + + public V replace(K key, V value) { + synchronized(mutex) { + return map.replace(key, value); + } + } + + private Set keySet=null; + private Set> entrySet=null; + private Collection values=null; + + + public Set keySet() { + synchronized(mutex) { + if (keySet == null) + keySet=Collections.synchronizedSet(map.keySet()); + return keySet; + } + } + + public Collection values() { + synchronized(mutex) { + if (values == null) + values=Collections.synchronizedCollection(map.values()); + return values; + } + } + + public Set> entrySet() { + synchronized(mutex) { + if (entrySet == null) + entrySet=Collections.synchronizedSet(map.entrySet()); + return entrySet; + } + } + + public V remove(Object key) { + synchronized(mutex) { + return map.remove(key); + } + } + + public V _put(K key, V value) { + synchronized(mutex) { + return map._put(key, value); + } + } + + public void _putAll(Map map) { + synchronized(mutex) { + this.map._putAll(map); + } + } + + public void _clear() { + synchronized(mutex) { + map._clear(); + } + } + + public V _remove(Object key) { + synchronized(mutex) { + return map._remove(key); + } + } + + public V _putIfAbsent(K key, V value) { + synchronized(mutex) { + return map._putIfAbsent(key, value); + } + } + + public boolean _remove(Object key, Object value) { + synchronized(mutex) { + return map._remove(key, value); + } + } + + public boolean _replace(K key, V oldValue, V newValue) { + synchronized(mutex) { + return map._replace(key, oldValue, newValue); + } + } + + public V _replace(K key, V value) { + synchronized(mutex) { + return map._replace(key, value); + } + } + + public String toString() { + synchronized(mutex) { + return map.toString(); + } + } + + public int hashCode() { + synchronized(mutex) { + return map.hashCode(); + } + } + + public boolean equals(Object obj) { + synchronized(mutex) { + return map.equals(obj); + } + } + + + } + + +} \ No newline at end of file Index: 3rdParty_sources/jgroups/org/jgroups/blocks/ReplicatedHashtable.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/blocks/ReplicatedHashtable.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/blocks/ReplicatedHashtable.java 17 Aug 2012 14:51:18 -0000 1.1 @@ -0,0 +1,510 @@ +// $Id: ReplicatedHashtable.java,v 1.1 2012/08/17 14:51:18 marcin Exp $ + +package org.jgroups.blocks; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.*; +import org.jgroups.util.Util; + +import java.io.Serializable; +import java.util.*; + +/** + * Provides the abstraction of a java.util.Hashtable that is replicated at several + * locations. Any change to the hashtable (clear, put, remove etc) will transparently be + * propagated to all replicas in the group. All read-only methods will always access the + * local replica.

+ * Both keys and values added to the hashtable must be serializable, the reason + * being that they will be sent across the network to all replicas of the group. Having said + * this, it is now for example possible to add RMI remote objects to the hashtable as they + * are derived from java.rmi.server.RemoteObject which in turn is serializable. + * This allows to lookup shared distributed objects by their name and invoke methods on them, + * regardless of one's onw location. A ReplicatedHashtable thus allows to + * implement a distributed naming service in just a couple of lines.

+ * An instance of this class will contact an existing member of the group to fetch its + * initial state.

+ * Contrary to DistributedHashtable, this class does not make use of RpcDispatcher (and RequestCorrelator) + * but uses plain asynchronous messages instead. + * @author Bela Ban + * @author Alfonso Olias-Sanz + * @deprecated Use {@link org.jgroups.blocks.ReplicatedHashMap} instead + */ +public class ReplicatedHashtable extends Hashtable implements MessageListener, MembershipListener { + + public interface Notification { + void entrySet(Object key, Object value); + + void entryRemoved(Object key); + + void viewChange(Vector new_mbrs, Vector old_mbrs); + + void contentsSet(Map new_entries); + } + + public interface StateTransferListener { + void stateTransferStarted(); + + void stateTransferCompleted(boolean success); + } + + transient Channel channel; + transient PullPushAdapter adapter=null; + final transient Vector notifs=new Vector(); + // to be notified when mbrship changes + final transient Vector members=new Vector(); // keeps track of all DHTs + final transient List state_transfer_listeners=new ArrayList(); + transient boolean state_transfer_running=false; + + /** Determines when the updates have to be sent across the network, avoids sending unnecessary + * messages when there are no member in the group */ + private transient boolean send_message=false; + + protected final transient Log log=LogFactory.getLog(this.getClass()); + + /** + * Creates a ReplicatedHashtable + * @param groupname The name of the group to join + * @param factory The ChannelFactory which will be used to create a channel + * @param properties The property string to be used to define the channel + * @param state_timeout The time to wait until state is retrieved in milliseconds. A value of 0 means wait forever. + */ + public ReplicatedHashtable(String groupname, ChannelFactory factory, StateTransferListener l, String properties, long state_timeout) { + if(l != null) + addStateTransferListener(l); + try { + channel=factory != null ? factory.createChannel((Object)properties) : new JChannel(properties); + channel.connect(groupname); + adapter=new PullPushAdapter(channel, this, this); + adapter.setListener(this); + getInitState(channel, state_timeout); + } + catch(Exception e) { + if(log.isErrorEnabled()) log.error("exception=" + e); + } + } + + private void getInitState(Channel channel, long state_timeout) throws ChannelClosedException, ChannelNotConnectedException { + try { + notifyStateTransferStarted(); + boolean rc=channel.getState(null, state_timeout); + if(rc) { + if(log.isInfoEnabled()) log.info("state was retrieved successfully"); + } + else { + if(log.isInfoEnabled()) log.info("state could not be retrieved (first member)"); + notifyStateTransferCompleted(false); + } + } + catch(ChannelClosedException ex) { + notifyStateTransferCompleted(false); + throw ex; + } + catch(ChannelNotConnectedException ex2) { + notifyStateTransferCompleted(false); + throw ex2; + } + } + + public ReplicatedHashtable(String groupname, ChannelFactory factory, String properties, long state_timeout) { + this(groupname, factory, null, properties, state_timeout); + } + + public ReplicatedHashtable(JChannel channel, long state_timeout) throws ChannelClosedException, ChannelNotConnectedException { + this(channel, null, state_timeout); + } + + public ReplicatedHashtable(JChannel channel, StateTransferListener l, long state_timeout) throws ChannelClosedException, ChannelNotConnectedException { + this.channel=channel; + this.adapter=new PullPushAdapter(channel, this, this); + this.adapter.setListener(this); + if(l != null) + addStateTransferListener(l); + getInitState(channel, state_timeout); +// boolean rc=channel.getState(null, state_timeout); +// if(rc) +// if(log.isInfoEnabled()) log.info("state was retrieved successfully"); +// else +// if(log.isInfoEnabled()) log.info("state could not be retrieved (first member)"); + } + + public boolean stateTransferRunning() { + return state_transfer_running; + } + + public Address getLocalAddress() { + return channel != null ? channel.getLocalAddress() : null; + } + + public Channel getChannel() { + return channel; + } + + public void addNotifier(Notification n) { + if(!notifs.contains(n)) + notifs.addElement(n); + } + + public final void addStateTransferListener(StateTransferListener l) { + if(l != null && !(state_transfer_listeners.contains(l))) + state_transfer_listeners.add(l); + } + + public void removeStateTransferListener(StateTransferListener l) { + if(l != null) + state_transfer_listeners.remove(l); + } + + /** + * Maps the specified key to the specified value in the hashtable. Neither of both parameters can be null + * @param key - the hashtable key + * @param value - the value + * @return the previous value of the specified key in this hashtable, or null if it did not have one + */ + public Object put(Object key, Object value) { + Message msg; + Object prev_val=null; + prev_val=get(key); + + //Changes done by + //if true, send message to the group + if(send_message == true) { + try { + msg=new Message(null, null, new Request(Request.PUT, key, value)); + channel.send(msg); + //return prev_val; + } + catch(Exception e) { + //return null; + } + } + else { + super.put(key, value); + //don't have to do prev_val = super.put(..) as is done at the beginning + } + return prev_val; + } + + /** + * Copies all of the mappings from the specified Map to this Hashtable These mappings will replace any mappings that this Hashtable had for any of the keys currently in the specified Map. + * @param m - Mappings to be stored in this map + */ + public void putAll(Map m) { + Message msg; + //Changes done by + //if true, send message to the group + if(send_message == true) { + try { + msg=new Message(null, null, new Request(Request.PUT_ALL, null, m)); + channel.send(msg); + } + catch(Exception e) { + if(log.isErrorEnabled()) log.error("exception=" + e); + } + } + else { + super.putAll(m); + } + } + + /** + * Clears this hashtable so that it contains no keys + */ + public void clear() { + Message msg; + //Changes done by + //if true, send message to the group + if(send_message == true) { + try { + msg=new Message(null, null, new Request(Request.CLEAR, null, null)); + channel.send(msg); + } + catch(Exception e) { + if(log.isErrorEnabled()) log.error("exception=" + e); + } + } + else { + super.clear(); + } + } + + /** + * Removes the key (and its corresponding value) from the Hashtable. + * @param key - the key to be removed. + * @return the value to which the key had been mapped in this hashtable, or null if the key did not have a mapping. + */ + public Object remove(Object key) { + Message msg; + Object retval=null; + retval=get(key); + + //Changes done by + //if true, propagate action to the group + if(send_message == true) { + try { + msg=new Message(null, null, new Request(Request.REMOVE, key, null)); + channel.send(msg); + //return retval; + } + catch(Exception e) { + //return null; + } + } + else { + super.remove(key); + //don't have to do retval = super.remove(..) as is done at the beginning + } + return retval; + } + + /*------------------------ Callbacks -----------------------*/ + Object _put(Object key, Object value) { + Object retval=super.put(key, value); + for(int i=0; i < notifs.size(); i++) + ((Notification)notifs.elementAt(i)).entrySet(key, value); + return retval; + } + + void _clear() { + super.clear(); + } + + Object _remove(Object key) { + Object retval=super.remove(key); + for(int i=0; i < notifs.size(); i++) + ((Notification)notifs.elementAt(i)).entryRemoved(key); + return retval; + } + + /** + * @see java.util.Map#putAll(java.util.Map) + */ + public void _putAll(Map m) { + if(m == null) + return; + //######## The same way as in the DistributedHashtable + // Calling the method below seems okay, but would result in ... deadlock ! + // The reason is that Map.putAll() calls put(), which we override, which results in + // lock contention for the map. + // ---> super.putAll(m); <--- CULPRIT !!!@#$%$ + // That said let's do it the stupid way: + //######## The same way as in the DistributedHashtable + Map.Entry entry; + for(Iterator it=m.entrySet().iterator(); it.hasNext();) { + entry=(Map.Entry)it.next(); + super.put(entry.getKey(), entry.getValue()); + } + + for(int i=0; i < notifs.size(); i++) + ((Notification)notifs.elementAt(i)).contentsSet(m); + } + /*----------------------------------------------------------*/ + + /*-------------------- MessageListener ----------------------*/ + + public void receive(Message msg) { + Request req=null; + + if(msg == null) + return; + req=(Request)msg.getObject(); + if(req == null) + return; + switch(req.req_type) { + case Request.PUT: + if(req.key != null && req.val != null) + _put(req.key, req.val); + break; + case Request.REMOVE: + if(req.key != null) + _remove(req.key); + break; + case Request.CLEAR: + _clear(); + break; + + case Request.PUT_ALL: + if(req.val != null) + _putAll((Map)req.val); + break; + default : + // error + } + } + + public byte[] getState() { + Object key, val; + Hashtable copy=new Hashtable(); + + for(Enumeration e=keys(); e.hasMoreElements();) { + key=e.nextElement(); + val=get(key); + copy.put(key, val); + } + try { + return Util.objectToByteBuffer(copy); + } + catch(Exception ex) { + if(log.isErrorEnabled()) log.error("exception marshalling state: " + ex); + return null; + } + } + + public void setState(byte[] new_state) { + Hashtable new_copy; + Object key; + + try { + new_copy=(Hashtable)Util.objectFromByteBuffer(new_state); + if(new_copy == null) { + notifyStateTransferCompleted(true); + return; + } + } + catch(Throwable ex) { + if(log.isErrorEnabled()) log.error("exception unmarshalling state: " + ex); + notifyStateTransferCompleted(false); + return; + } + + _clear(); // remove all elements + for(Enumeration e=new_copy.keys(); e.hasMoreElements();) { + key=e.nextElement(); + _put(key, new_copy.get(key)); + } + notifyStateTransferCompleted(true); + } + + /*-------------------- End of MessageListener ----------------------*/ + + /*----------------------- MembershipListener ------------------------*/ + + public void viewAccepted(View new_view) { + Vector new_mbrs=new_view.getMembers(); + + if(new_mbrs != null) { + sendViewChangeNotifications(new_mbrs, members); + // notifies observers (joined, left) + members.removeAllElements(); + for(int i=0; i < new_mbrs.size(); i++) + members.addElement(new_mbrs.elementAt(i)); + } + //if size is bigger than one, there are more peers in the group + //otherwise there is only one server. + if(members.size() > 1) { + send_message=true; + } + else { + send_message=false; + } + } + + /** Called when a member is suspected */ + public void suspect(Address suspected_mbr) { + ; + } + + /** Block sending and receiving of messages until ViewAccepted is called */ + public void block() { + } + + /*------------------- End of MembershipListener ----------------------*/ + + void sendViewChangeNotifications(Vector new_mbrs, Vector old_mbrs) { + Vector joined, left; + Object mbr; + Notification n; + + if(notifs.size() == 0 || old_mbrs == null || new_mbrs == null || old_mbrs.size() == 0 || new_mbrs.size() == 0) + return; + + // 1. Compute set of members that joined: all that are in new_mbrs, but not in old_mbrs + joined=new Vector(); + for(int i=0; i < new_mbrs.size(); i++) { + mbr=new_mbrs.elementAt(i); + if(!old_mbrs.contains(mbr)) + joined.addElement(mbr); + } + + // 2. Compute set of members that left: all that were in old_mbrs, but not in new_mbrs + left=new Vector(); + for(int i=0; i < old_mbrs.size(); i++) { + mbr=old_mbrs.elementAt(i); + if(!new_mbrs.contains(mbr)) { + left.addElement(mbr); + } + } + + for(int i=0; i < notifs.size(); i++) { + n=(Notification)notifs.elementAt(i); + n.viewChange(joined, left); + } + } + + void notifyStateTransferStarted() { + state_transfer_running=true; + for(Iterator it=state_transfer_listeners.iterator(); it.hasNext();) { + StateTransferListener listener=(StateTransferListener)it.next(); + try { + listener.stateTransferStarted(); + } + catch(Throwable t) { + } + } + } + + void notifyStateTransferCompleted(boolean success) { + state_transfer_running=false; + for(Iterator it=state_transfer_listeners.iterator(); it.hasNext();) { + StateTransferListener listener=(StateTransferListener)it.next(); + try { + listener.stateTransferCompleted(success); + } + catch(Throwable t) { + } + } + } + + private static class Request implements Serializable { + static final int PUT=1; + static final int REMOVE=2; + static final int CLEAR=3; + static final int PUT_ALL=4; + + int req_type=0; + Object key=null; + Object val=null; + + Request(int req_type, Object key, Object val) { + this.req_type=req_type; + this.key=key; + this.val=val; + } + + public String toString() { + StringBuilder sb=new StringBuilder(); + sb.append(type2String(req_type)); + if(key != null) + sb.append("\nkey=" + key); + if(val != null) + sb.append("\nval=" + val); + return sb.toString(); + } + + String type2String(int t) { + switch(t) { + case PUT: + return "PUT"; + case REMOVE: + return "REMOVE"; + case CLEAR: + return "CLEAR"; + case PUT_ALL: + return "PUT_ALL"; + default : + return ""; + } + } + + } +} Index: 3rdParty_sources/jgroups/org/jgroups/blocks/ReplicatedMap.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/blocks/ReplicatedMap.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/blocks/ReplicatedMap.java 17 Aug 2012 14:51:18 -0000 1.1 @@ -0,0 +1,27 @@ +package org.jgroups.blocks; + +import java.io.Serializable; +import java.util.Map; +import java.util.concurrent.ConcurrentMap; + +/** + * @author Bela Ban + * @version $Id: ReplicatedMap.java,v 1.1 2012/08/17 14:51:18 marcin Exp $ + */ +public interface ReplicatedMap extends ConcurrentMap { + V _put(K key, V value); + + void _putAll(Map map); + + void _clear(); + + V _remove(Object key); + + V _putIfAbsent(K key, V value); + + boolean _remove(Object key, Object value); + + boolean _replace(K key, V oldValue, V newValue); + + V _replace(K key, V value); +} Index: 3rdParty_sources/jgroups/org/jgroups/blocks/ReplicatedTree.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/blocks/ReplicatedTree.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/blocks/ReplicatedTree.java 17 Aug 2012 14:51:18 -0000 1.1 @@ -0,0 +1,1071 @@ +// $Id: ReplicatedTree.java,v 1.1 2012/08/17 14:51:18 marcin Exp $ + +package org.jgroups.blocks; + + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.*; +import org.jgroups.jmx.JmxConfigurator; +import org.jgroups.util.Queue; +import org.jgroups.util.QueueClosedException; +import org.jgroups.util.Util; + +import javax.management.MBeanServer; +import java.io.Serializable; +import java.util.*; + + + + +/** + * A tree-like structure that is replicated across several members. Updates will be multicast to all group + * members reliably and in the same order. + * @author Bela Ban Jan 17 2002 + * @author Alfonso Olias-Sanz + */ +public class ReplicatedTree implements Runnable, MessageListener, MembershipListener { + public static final String SEPARATOR="/"; + final static int INDENT=4; + Node root=new Node(SEPARATOR, SEPARATOR, null, null); + final Vector listeners=new Vector(); + final Queue request_queue=new Queue(); + Thread request_handler=null; + JChannel channel=null; + PullPushAdapter adapter=null; + String groupname="ReplicatedTree-Group"; + final Vector members=new Vector(); + long state_fetch_timeout=10000; + boolean jmx=false; + + protected final Log log=LogFactory.getLog(this.getClass()); + + + /** Whether or not to use remote calls. If false, all methods will be invoked directly on this + instance rather than sending a message to all replicas and only then invoking the method. + Useful for testing */ + boolean remote_calls=true; + String props="udp.xml"; + + /** Determines when the updates have to be sent across the network, avoids sending unnecessary + * messages when there are no member in the group */ + private boolean send_message = false; + + + + public interface ReplicatedTreeListener { + void nodeAdded(String fqn); + + void nodeRemoved(String fqn); + + void nodeModified(String fqn); + + void viewChange(View new_view); // might be MergeView after merging + } + + + /** + * Creates a channel with the given properties. Connects to the channel, then creates a PullPushAdapter + * and starts it + */ + public ReplicatedTree(String groupname, String props, long state_fetch_timeout) throws Exception { + if(groupname != null) + this.groupname=groupname; + if(props != null) + this.props=props; + this.state_fetch_timeout=state_fetch_timeout; + channel=new JChannel(this.props); + channel.connect(this.groupname); + start(); + } + + public ReplicatedTree(String groupname, String props, long state_fetch_timeout, boolean jmx) throws Exception { + if(groupname != null) + this.groupname=groupname; + if(props != null) + this.props=props; + this.jmx=jmx; + this.state_fetch_timeout=state_fetch_timeout; + channel=new JChannel(this.props); + channel.connect(this.groupname); + if(jmx) { + MBeanServer server=Util.getMBeanServer(); + if(server == null) + throw new Exception("No MBeanServers found; need to run with an MBeanServer present, or inside JDK 5"); + JmxConfigurator.registerChannel(channel, server, "jgroups", channel.getClusterName() , true); + } + start(); + } + + public ReplicatedTree() { + } + + + /** + * Expects an already connected channel. Creates a PullPushAdapter and starts it + */ + public ReplicatedTree(JChannel channel) throws Exception { + this.channel=channel; + start(); + } + + + public void setRemoteCalls(boolean flag) { + remote_calls=flag; + } + + public void setRootNode(Node n) { + root=n; + } + + public Address getLocalAddress() { + return channel != null? channel.getLocalAddress() : null; + } + + public Vector getMembers() { + return members; + } + + + /** + * Fetch the group state from the current coordinator. If successful, this will trigger setState(). + */ + public void fetchState(long timeout) throws ChannelClosedException, ChannelNotConnectedException { + boolean rc=channel.getState(null, timeout); + if(log.isInfoEnabled()) { + if(rc) + log.info("state was retrieved successfully"); + else + log.info("state could not be retrieved (first member)"); + } + } + + + public void addReplicatedTreeListener(ReplicatedTreeListener listener) { + if(!listeners.contains(listener)) + listeners.addElement(listener); + } + + + public void removeReplicatedTreeListener(ReplicatedTreeListener listener) { + listeners.removeElement(listener); + } + + + public final void start() throws Exception { + if(request_handler == null) { + request_handler=new Thread(this, "ReplicatedTree.RequestHandler thread"); + request_handler.setDaemon(true); + request_handler.start(); + } + adapter=new PullPushAdapter(channel, this, this); + adapter.setListener(this); + boolean rc=channel.getState(null, state_fetch_timeout); + + if(log.isInfoEnabled()) { + if(rc) + log.info("state was retrieved successfully"); + else + log.info("state could not be retrieved (first member)"); + } + } + + + public void stop() { + if(request_handler != null && request_handler.isAlive()) { + request_queue.close(true); + request_handler=null; + } + + request_handler=null; + if(channel != null) { + channel.close(); + } + if(adapter != null) { + adapter.stop(); + adapter=null; + } + channel=null; + } + + + /** + * Adds a new node to the tree and sets its data. If the node doesn not yet exist, it will be created. + * Also, parent nodes will be created if not existent. If the node already has data, then the new data + * will override the old one. If the node already existed, a nodeModified() notification will be generated. + * Otherwise a nodeCreated() motification will be emitted. + * @param fqn The fully qualified name of the new node + * @param data The new data. May be null if no data should be set in the node. + */ + public void put(String fqn, HashMap data) { + if(!remote_calls) { + _put(fqn, data); + return; + } + + //Changes done by + //if true, propagate action to the group + if(send_message == true) { + if(channel == null) { + if(log.isErrorEnabled()) log.error("channel is null, cannot broadcast PUT request"); + return; + } + try { + channel.send( + new Message( + null, + null, + new Request(Request.PUT, fqn, data))); + } + catch(Exception ex) { + if(log.isErrorEnabled()) log.error("failure bcasting PUT request: " + ex); + } + } + else { + _put(fqn, data); + } + } + + + /** + * Adds a key and value to a given node. If the node doesn't exist, it will be created. If the node + * already existed, a nodeModified() notification will be generated. Otherwise a + * nodeCreated() motification will be emitted. + * @param fqn The fully qualified name of the node + * @param key The key + * @param value The value + */ + public void put(String fqn, String key, Object value) { + if(!remote_calls) { + _put(fqn, key, value); + return; + } + + //Changes done by + //if true, propagate action to the group + if(send_message == true) { + + if(channel == null) { + if(log.isErrorEnabled()) log.error("channel is null, cannot broadcast PUT request"); + return; + } + try { + channel.send( + new Message( + null, + null, + new Request(Request.PUT, fqn, key, value))); + } + catch(Exception ex) { + if(log.isErrorEnabled()) log.error("failure bcasting PUT request: " + ex); + } + } + else { + _put(fqn, key, value); + } + } + + + /** + * Removes the node from the tree. + * @param fqn The fully qualified name of the node. + */ + public void remove(String fqn) { + if(!remote_calls) { + _remove(fqn); + return; + } + //Changes done by + //if true, propagate action to the group + if(send_message == true) { + if(channel == null) { + if(log.isErrorEnabled()) log.error("channel is null, cannot broadcast REMOVE request"); + return; + } + try { + channel.send( + new Message(null, null, new Request(Request.REMOVE, fqn))); + } + catch(Exception ex) { + if(log.isErrorEnabled()) log.error("failure bcasting REMOVE request: " + ex); + } + } + else { + _remove(fqn); + } + } + + + /** + * Removes key from the node's hashmap + * @param fqn The fullly qualified name of the node + * @param key The key to be removed + */ + public void remove(String fqn, String key) { + if(!remote_calls) { + _remove(fqn, key); + return; + } + //Changes done by + //if true, propagate action to the group + if(send_message == true) { + if(channel == null) { + if(log.isErrorEnabled()) log.error("channel is null, cannot broadcast REMOVE request"); + return; + } + try { + channel.send( + new Message( + null, + null, + new Request(Request.REMOVE, fqn, key))); + } + catch(Exception ex) { + if(log.isErrorEnabled()) log.error("failure bcasting REMOVE request: " + ex); + } + } + else { + _remove(fqn, key); + } + } + + + /** + * Checks whether a given node exists in the tree + * @param fqn The fully qualified name of the node + * @return boolean Whether or not the node exists + */ + public boolean exists(String fqn) { + if(fqn == null) return false; + return findNode(fqn) != null; + } + + + /** + * Gets the keys of the data map. Returns all keys as Strings. Returns null if node + * does not exist. + * @param fqn The fully qualified name of the node + * @return Set A set of keys (as Strings) + */ + public Set getKeys(String fqn) { + Node n=findNode(fqn); + Map data; + + if(n == null) return null; + data=n.getData(); + if(data == null) return null; + return data.keySet(); + } + + + /** + * Finds a node given its name and returns the value associated with a given key in its data + * map. Returns null if the node was not found in the tree or the key was not found in the hashmap. + * @param fqn The fully qualified name of the node. + * @param key The key. + */ + public Object get(String fqn, String key) { + Node n=findNode(fqn); + + if(n == null) return null; + return n.getData(key); + } + + + /** + * Returns the data hashmap for a given node. This method can only be used by callers that are inside + * the same package. The reason is that callers must not modify the return value, as these modifications + * would not be replicated, thus rendering the replicas inconsistent. + * @param fqn The fully qualified name of the node + * @return HashMap The data hashmap for the given node + */ + HashMap get(String fqn) { + Node n=findNode(fqn); + + if(n == null) return null; + return n.getData(); + } + + + /** + * Prints a representation of the node defined by fqn. Output includes name, fqn and + * data. + */ + public String print(String fqn) { + Node n=findNode(fqn); + if(n == null) return null; + return n.toString(); + } + + + /** + * Returns all children of a given node + * @param fqn The fully qualified name of the node + * @return Set A list of child names (as Strings) + */ + public Set getChildrenNames(String fqn) { + Node n=findNode(fqn); + Map m; + + if(n == null) return null; + m=n.getChildren(); + if(m != null) + return m.keySet(); + else + return null; + } + + + public String toString() { + StringBuilder sb=new StringBuilder(); + int indent=0; + Map children; + + children=root.getChildren(); + if(children != null && children.size() > 0) { + Collection nodes=children.values(); + for(Iterator it=nodes.iterator(); it.hasNext();) { + ((Node)it.next()).print(sb, indent); + sb.append('\n'); + } + } + else + sb.append(SEPARATOR); + return sb.toString(); + } + + /** + * Returns the name of the group that the DistributedTree is connected to + * @return String + */ + public String getGroupName() {return groupname;} + + /** + * Returns the Channel the DistributedTree is connected to + * @return Channel + */ + public Channel getChannel() {return channel;} + + /** + * Returns the number of current members joined to the group + * @return int + */ + public int getGroupMembersNumber() {return members.size();} + + + + + /* --------------------- Callbacks -------------------------- */ + + + public void _put(String fqn, HashMap data) { + Node n; + StringHolder child_name=new StringHolder(); + boolean child_exists=false; + + if(fqn == null) return; + n=findParentNode(fqn, child_name, true); // create all nodes if they don't exist + if(child_name.getValue() != null) { + child_exists=n.childExists(child_name.getValue()); + n.createChild(child_name.getValue(), fqn, n, data); + } + else { + child_exists=true; + n.setData(data); + } + if(child_exists) + notifyNodeModified(fqn); + else + notifyNodeAdded(fqn); + } + + + public void _put(String fqn, String key, Object value) { + Node n; + StringHolder child_name=new StringHolder(); + boolean child_exists=false; + + if(fqn == null || key == null || value == null) return; + n=findParentNode(fqn, child_name, true); + if(child_name.getValue() != null) { + child_exists=n.childExists(child_name.getValue()); + n.createChild(child_name.getValue(), fqn, n, key, value); + } + else { + child_exists=true; + n.setData(key, value); + } + if(child_exists) + notifyNodeModified(fqn); + else + notifyNodeAdded(fqn); + } + + + public void _remove(String fqn) { + Node n; + StringHolder child_name=new StringHolder(); + + if(fqn == null) return; + if(fqn.equals(SEPARATOR)) { + root.removeAll(); + notifyNodeRemoved(fqn); + return; + } + n=findParentNode(fqn, child_name, false); + if(n == null) return; + n.removeChild(child_name.getValue(), fqn); + notifyNodeRemoved(fqn); + } + + + public void _remove(String fqn, String key) { + Node n; + + if(fqn == null || key == null) return; + n=findNode(fqn); + if(n != null) + n.removeData(key); + } + + + public void _removeData(String fqn) { + Node n; + + if(fqn == null) return; + n=findNode(fqn); + if(n != null) + n.removeData(); + } + + + /* ----------------- End of Callbacks ---------------------- */ + + + + + + + /*-------------------- MessageListener ----------------------*/ + + /** Callback. Process the contents of the message; typically an _add() or _set() request */ + public void receive(Message msg) { + Request req=null; + + if(msg == null || msg.getLength() == 0) + return; + try { + req=(Request)msg.getObject(); + request_queue.add(req); + } + catch(QueueClosedException queue_closed_ex) { + if(log.isErrorEnabled()) log.error("request queue is null"); + } + catch(Exception ex) { + if(log.isErrorEnabled()) log.error("failed unmarshalling request: " + ex); + } + } + + /** Return a copy of the current cache (tree) */ + public byte[] getState() { + try { + return Util.objectToByteBuffer(root.clone()); + } + catch(Throwable ex) { + if(log.isErrorEnabled()) log.error("exception returning cache: " + ex); + return null; + } + } + + /** Set the cache (tree) to this value */ + public void setState(byte[] new_state) { + Node new_root=null; + Object obj; + + if(new_state == null) { + if(log.isInfoEnabled()) log.info("new cache is null"); + return; + } + try { + obj=Util.objectFromByteBuffer(new_state); + new_root=(Node)((Node)obj).clone(); + root=new_root; + notifyAllNodesCreated(root); + } + catch(Throwable ex) { + if(log.isErrorEnabled()) log.error("could not set cache: " + ex); + } + } + + /*-------------------- End of MessageListener ----------------------*/ + + + + + + /*----------------------- MembershipListener ------------------------*/ + + public void viewAccepted(View new_view) { + Vector new_mbrs=new_view.getMembers(); + + // todo: if MergeView, fetch and reconcile state from coordinator + // actually maybe this is best left up to the application ? we just notify them and let + // the appl handle it ? + + if(new_mbrs != null) { + notifyViewChange(new_view); + members.removeAllElements(); + for(int i=0; i < new_mbrs.size(); i++) + members.addElement(new_mbrs.elementAt(i)); + } + //if size is bigger than one, there are more peers in the group + //otherwise there is only one server. + send_message=members.size() > 1; + } + + + /** Called when a member is suspected */ + public void suspect(Address suspected_mbr) { + ; + } + + + /** Block sending and receiving of messages until viewAccepted() is called */ + public void block() { + } + + /*------------------- End of MembershipListener ----------------------*/ + + + + /** Request handler thread */ + public void run() { + Request req; + String fqn=null; + + while(request_handler != null) { + try { + req=(Request)request_queue.remove(0); + fqn=req.fqn; + switch(req.type) { + case Request.PUT: + if(req.key != null && req.value != null) + _put(fqn, req.key, req.value); + else + _put(fqn, req.data); + break; + case Request.REMOVE: + if(req.key != null) + _remove(fqn, req.key); + else + _remove(fqn); + break; + default: + if(log.isErrorEnabled()) log.error("type " + req.type + " unknown"); + break; + } + } + catch(QueueClosedException queue_closed_ex) { + request_handler=null; + break; + } + catch(Throwable other_ex) { + if(log.isWarnEnabled()) log.warn("exception processing request: " + other_ex); + } + } + } + + + /** + * Find the node just above the one indicated by fqn. This is needed in many cases, + * e.g. to add a new node or remove an existing node. + * @param fqn The fully qualified name of the node. + * @param child_name Will be filled with the name of the child when this method returns. The child name + * is the last relative name of the fqn, e.g. in "/a/b/c" it would be "c". + * @param create_if_not_exists Create parent nodes along the way if they don't exist. Otherwise, this method + * will return when a node cannot be found. + */ + Node findParentNode(String fqn, StringHolder child_name, boolean create_if_not_exists) { + Node curr=root, node; + StringTokenizer tok; + String name; + StringBuilder sb=null; + + if(fqn == null || fqn.equals(SEPARATOR) || "".equals(fqn)) + return curr; + + sb=new StringBuilder(); + tok=new StringTokenizer(fqn, SEPARATOR); + while(tok.countTokens() > 1) { + name=tok.nextToken(); + sb.append(SEPARATOR).append(name); + node=curr.getChild(name); + if(node == null && create_if_not_exists) + node=curr.createChild(name, sb.toString(), null, null); + if(node == null) + return null; + else + curr=node; + } + + if(tok.countTokens() > 0 && child_name != null) + child_name.setValue(tok.nextToken()); + return curr; + } + + + /** + * Returns the node at fqn. This method should not be used by clients (therefore it is package-private): + * it is only used internally (for navigation). C++ 'friend' would come in handy here... + * @param fqn The fully qualified name of the node + * @return Node The node at fqn + */ + Node findNode(String fqn) { + StringHolder sh=new StringHolder(); + Node n=findParentNode(fqn, sh, false); + String child_name=sh.getValue(); + + if(fqn == null || fqn.equals(SEPARATOR) || "".equals(fqn)) + return root; + + if(n == null || child_name == null) + return null; + else + return n.getChild(child_name); + } + + + void notifyNodeAdded(String fqn) { + for(int i=0; i < listeners.size(); i++) + ((ReplicatedTreeListener)listeners.elementAt(i)).nodeAdded(fqn); + } + + void notifyNodeRemoved(String fqn) { + for(int i=0; i < listeners.size(); i++) + ((ReplicatedTreeListener)listeners.elementAt(i)).nodeRemoved(fqn); + } + + void notifyNodeModified(String fqn) { + for(int i=0; i < listeners.size(); i++) + ((ReplicatedTreeListener)listeners.elementAt(i)).nodeModified(fqn); + } + + void notifyViewChange(View v) { + for(int i=0; i < listeners.size(); i++) + ((ReplicatedTreeListener)listeners.elementAt(i)).viewChange(v); + } + + /** Generates NodeAdded notifications for all nodes of the tree. This is called whenever the tree is + initially retrieved (state transfer) */ + void notifyAllNodesCreated(Node curr) { + Node n; + Map children; + + if(curr == null) return; + notifyNodeAdded(curr.fqn); + if((children=curr.getChildren()) != null) { + for(Iterator it=children.values().iterator(); it.hasNext();) { + n=(Node)it.next(); + notifyAllNodesCreated(n); + } + } + } + + + public static class Node implements Serializable { + String name=null; // relative name (e.g. "Security") + String fqn=null; // fully qualified name (e.g. "/federations/fed1/servers/Security") + Node parent=null; // parent node + TreeMap children=null; // keys: child name, value: Node + HashMap data=null; // data for current node + private static final long serialVersionUID = -3077676554440038890L; + // Address creator=null; // member that created this node (needed ?) + + + private Node(String child_name, String fqn, Node parent, HashMap data) { + name=child_name; + this.fqn=fqn; + this.parent=parent; + if(data != null) this.data=(HashMap)data.clone(); + } + + private Node(String child_name, String fqn, Node parent, String key, Object value) { + name=child_name; + this.fqn=fqn; + this.parent=parent; + if(data == null) data=new HashMap(); + data.put(key, value); + } + + void setData(Map data) { + if(data == null) return; + if(this.data == null) + this.data=new HashMap(); + this.data.putAll(data); + } + + void setData(String key, Object value) { + if(this.data == null) + this.data=new HashMap(); + this.data.put(key, value); + } + + HashMap getData() { + return data; + } + + Object getData(String key) { + return data != null? data.get(key) : null; + } + + + boolean childExists(String child_name) { + if(child_name == null) return false; + return children != null && children.containsKey(child_name); + } + + + Node createChild(String child_name, String fqn, Node parent, HashMap data) { + Node child=null; + + if(child_name == null) return null; + if(children == null) children=new TreeMap(); + child=(Node)children.get(child_name); + if(child != null) + child.setData(data); + else { + child=new Node(child_name, fqn, parent, data); + children.put(child_name, child); + } + return child; + } + + Node createChild(String child_name, String fqn, Node parent, String key, Object value) { + Node child=null; + + if(child_name == null) return null; + if(children == null) children=new TreeMap(); + child=(Node)children.get(child_name); + if(child != null) + child.setData(key, value); + else { + child=new Node(child_name, fqn, parent, key, value); + children.put(child_name, child); + } + return child; + } + + + Node getChild(String child_name) { + return child_name == null? null : children == null? null : (Node)children.get(child_name); + } + + Map getChildren() { + return children; + } + + void removeData(String key) { + if(data != null) + data.remove(key); + } + + void removeData() { + if(data != null) + data.clear(); + } + + void removeChild(String child_name, String fqn) { + if(child_name != null && children != null && children.containsKey(child_name)) { + children.remove(child_name); + } + } + + void removeAll() { + if(children != null) + children.clear(); + } + + void print(StringBuilder sb, int indent) { + printIndent(sb, indent); + sb.append(SEPARATOR).append(name); + if(children != null && children.size() > 0) { + Collection values=children.values(); + for(Iterator it=values.iterator(); it.hasNext();) { + sb.append('\n'); + ((Node)it.next()).print(sb, indent + INDENT); + } + } + } + + void printIndent(StringBuilder sb, int indent) { + if(sb != null) { + for(int i=0; i < indent; i++) + sb.append(' '); + } + } + + + public String toString() { + StringBuilder sb=new StringBuilder(); + if(name != null) sb.append("\nname=" + name); + if(fqn != null) sb.append("\nfqn=" + fqn); + if(data != null) sb.append("\ndata=" + data); + return sb.toString(); + } + + + public Object clone() throws CloneNotSupportedException { + Node n=new Node(name, fqn, parent != null? (Node)parent.clone() : null, data); + if(children != null) n.children=(TreeMap)children.clone(); + return n; + } + + } + + + private static class StringHolder { + String s=null; + + private StringHolder() { + } + + void setValue(String s) { + this.s=s; + } + + String getValue() { + return s; + } + } + + + /** + * Class used to multicast add(), remove() and set() methods to all members. + */ + private static class Request implements Serializable { + static final int PUT=1; + static final int REMOVE=2; + + int type=0; + String fqn=null; + String key=null; + Object value=null; + HashMap data=null; + private static final long serialVersionUID = 7772753222127676782L; + + private Request(int type, String fqn) { + this.type=type; + this.fqn=fqn; + } + + private Request(int type, String fqn, HashMap data) { + this(type, fqn); + this.data=data; + } + + private Request(int type, String fqn, String key) { + this(type, fqn); + this.key=key; + } + + private Request(int type, String fqn, String key, Object value) { + this(type, fqn); + this.key=key; + this.value=value; + } + + public String toString() { + StringBuilder sb=new StringBuilder(); + sb.append(type2String(type)).append(" ("); + if(fqn != null) sb.append(" fqn=" + fqn); + switch(type) { + case PUT: + if(data != null) sb.append(", data=" + data); + if(key != null) sb.append(", key=" + key); + if(value != null) sb.append(", value=" + value); + break; + case REMOVE: + if(key != null) sb.append(", key=" + key); + break; + default: + break; + } + sb.append(')'); + return sb.toString(); + } + + static String type2String(int t) { + switch(t) { + case PUT: + return "PUT"; + case REMOVE: + return "REMOVE"; + default: + return "UNKNOWN"; + } + } + + } + + + public static void main(String[] args) { + + + ReplicatedTree tree=null; + HashMap m=new HashMap(); + String props; + + props="udp.xml"; + + try { + + tree=new ReplicatedTree(null, props, 10000); + // tree.setRemoteCalls(false); + tree.addReplicatedTreeListener(new MyListener()); + tree.put("/a/b/c", null); + tree.put("/a/b/c1", null); + tree.put("/a/b/c2", null); + tree.put("/a/b1/chat", null); + tree.put("/a/b1/chat2", null); + tree.put("/a/b1/chat5", null); + System.out.println(tree); + m.put("name", "Bela Ban"); + m.put("age", new Integer(36)); + m.put("cube", "240-17"); + tree.put("/a/b/c", m); + System.out.println("info for for \"/a/b/c\" is " + tree.print("/a/b/c")); + tree.put("/a/b/c", "age", new Integer(37)); + System.out.println("info for for \"/a/b/c\" is " + tree.print("/a/b/c")); + tree.remove("/a/b"); + System.out.println(tree); + } + catch(Exception ex) { + System.err.println(ex); + } + } + + + static class MyListener implements ReplicatedTreeListener { + + public void nodeAdded(String fqn) { + System.out.println("** node added: " + fqn); + } + + public void nodeRemoved(String fqn) { + System.out.println("** node removed: " + fqn); + } + + public void nodeModified(String fqn) { + System.out.println("** node modified: " + fqn); + } + + public void viewChange(View new_view) { + System.out.println("** view change: " + new_view); + } + + } + + +} + + Index: 3rdParty_sources/jgroups/org/jgroups/blocks/RequestCorrelator.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/blocks/RequestCorrelator.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/blocks/RequestCorrelator.java 17 Aug 2012 14:51:17 -0000 1.1 @@ -0,0 +1,954 @@ +// $Id: RequestCorrelator.java,v 1.1 2012/08/17 14:51:17 marcin Exp $ + +package org.jgroups.blocks; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.*; +import org.jgroups.protocols.TP; +import org.jgroups.stack.Protocol; +import org.jgroups.util.*; + +import java.io.*; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + + +/** + * Framework to send requests and receive matching responses (matching on + * request ID). + * Multiple requests can be sent at a time. Whenever a response is received, + * the correct RspCollector is looked up (key = id) and its + * method receiveResponse() invoked. A caller may use + * done() to signal that no more responses are expected, and that + * the corresponding entry may be removed. + *

+ * RequestCorrelator can be installed at both client and server + * sides, it can also switch roles dynamically; i.e., send a request and at + * the same time process an incoming request (when local delivery is enabled, + * this is actually the default). + *

+ * + * @author Bela Ban + */ +public class RequestCorrelator { + + /** The protocol layer to use to pass up/down messages. Can be either a Protocol or a Transport */ + protected Object transport=null; + + /** The table of pending requests (keys=Long (request IDs), values=RequestEntry) */ + protected final ConcurrentMap requests=new ConcurrentHashMap(); + + + /** The handler for the incoming requests. It is called from inside the dispatcher thread */ + protected RequestHandler request_handler=null; + + /** Possibility for an external marshaller to marshal/unmarshal responses */ + protected RpcDispatcher.Marshaller2 marshaller=null; + + /** makes the instance unique (together with IDs) */ + protected String name=null; + + /** The dispatching thread pool */ + protected Scheduler scheduler=null; + + + /** The address of this group member */ + protected Address local_addr=null; + + /** + * This field is used only if deadlock detection is enabled. + * In case of nested synchronous requests, it holds a list of the + * addreses of the senders with the address at the bottom being the + * address of the first caller + */ + protected java.util.Stack

call_stack=null; + + /** Whether or not to perform deadlock detection for synchronous (potentially recursive) group method invocations. + * If on, we use a scheduler (handling a priority queue), otherwise we don't and call handleRequest() directly. + */ + protected boolean deadlock_detection=false; + + /** + * This field is used only if deadlock detection is enabled. + * It sets the calling stack to the currently running request + */ + private CallStackSetter call_stack_setter=null; + + /** Process items on the queue concurrently (Scheduler). The default is to wait until the processing of an item + * has completed before fetching the next item from the queue. Note that setting this to true + * may destroy the properties of a protocol stack, e.g total or causal order may not be + * guaranteed. Set this to true only if you know what you're doing ! */ + protected boolean concurrent_processing=false; + + + protected boolean started=false; + + private final MyProbeHandler probe_handler=new MyProbeHandler(requests); + + protected static final Log log=LogFactory.getLog(RequestCorrelator.class); + + + /** + * Constructor. Uses transport to send messages. If handler + * is not null, all incoming requests will be dispatched to it (via + * handle(Message)). + * + * @param name Used to differentiate between different RequestCorrelators + * (e.g. in different protocol layers). Has to be unique if multiple + * request correlators are used. + * + * @param transport Used to send/pass up requests. Can be either a Transport (only send() will be + * used then), or a Protocol (up_prot.up()/down_prot.down() will be used) + * + * @param handler Request handler. Method handle(Message) + * will be called when a request is received. + */ + public RequestCorrelator(String name, Object transport, RequestHandler handler) { + this.name = name; + this.transport = transport; + request_handler = handler; + start(); + } + + + public RequestCorrelator(String name, Object transport, RequestHandler handler, Address local_addr) { + this.name = name; + this.transport = transport; + this.local_addr=local_addr; + request_handler = handler; + start(); + } + + + /** + * Constructor. Uses transport to send messages. If handler + * is not null, all incoming requests will be dispatched to it (via + * handle(Message)). + * + * @param name Used to differentiate between different RequestCorrelators + * (e.g. in different protocol layers). Has to be unique if multiple + * request correlators are used. + * + * @param transport Used to send/pass up requests. Can be either a Transport (only send() will be + * used then), or a Protocol (up_prot.up()/down_prot.down() will be used) + * + * @param handler Request handler. Method handle(Message) + * will be called when a request is received. + * + * @param deadlock_detection When enabled (true) recursive synchronous + * message calls will be detected and processed with higher priority in + * order to solve deadlocks. Slows down processing a little bit when + * enabled due to runtime checks involved. + */ + public RequestCorrelator(String name, Object transport, + RequestHandler handler, boolean deadlock_detection) { + this.deadlock_detection = deadlock_detection; + this.name = name; + this.transport = transport; + request_handler = handler; + start(); + } + + + public RequestCorrelator(String name, Object transport, + RequestHandler handler, boolean deadlock_detection, boolean concurrent_processing) { + this.deadlock_detection = deadlock_detection; + this.name = name; + this.transport = transport; + request_handler = handler; + this.concurrent_processing = concurrent_processing; + start(); + } + + public RequestCorrelator(String name, Object transport, + RequestHandler handler, boolean deadlock_detection, Address local_addr) { + this.deadlock_detection = deadlock_detection; + this.name = name; + this.transport = transport; + this.local_addr = local_addr; + request_handler = handler; + start(); + } + + public RequestCorrelator(String name, Object transport, RequestHandler handler, + boolean deadlock_detection, Address local_addr, boolean concurrent_processing) { + this.deadlock_detection = deadlock_detection; + this.name = name; + this.transport = transport; + this.local_addr = local_addr; + request_handler = handler; + this.concurrent_processing = concurrent_processing; + start(); + } + + + + + /** + * Switch the deadlock detection mechanism on/off + * @param flag the deadlock detection flag + */ + public void setDeadlockDetection(boolean flag) { + if(deadlock_detection != flag) { // only set it if different + deadlock_detection=flag; + if(started) { + if(deadlock_detection) { + startScheduler(); + } + else { + stopScheduler(); + } + } + } + } + + + public void setRequestHandler(RequestHandler handler) { + request_handler=handler; + start(); + } + + + public void setConcurrentProcessing(boolean concurrent_processing) { + this.concurrent_processing=concurrent_processing; + } + + + /** + * Helper method for {@link #sendRequest(long,List,Message,RspCollector)}. + */ + public void sendRequest(long id, Message msg, RspCollector coll) throws Exception { + sendRequest(id, null, msg, coll); + } + + + public RpcDispatcher.Marshaller getMarshaller() { + return marshaller; + } + + public void setMarshaller(RpcDispatcher.Marshaller marshaller) { + if(marshaller == null) + this.marshaller=null; + else if(marshaller instanceof RpcDispatcher.Marshaller2) + this.marshaller=(RpcDispatcher.Marshaller2)marshaller; + else + this.marshaller=new RpcDispatcher.MarshallerAdapter(marshaller); + } + + public void sendRequest(long id, List
dest_mbrs, Message msg, RspCollector coll) throws Exception { + sendRequest(id, dest_mbrs, msg, coll, false); + } + + /** + * Send a request to a group. If no response collector is given, no + * responses are expected (making the call asynchronous). + * + * @param id The request ID. Must be unique for this JVM (e.g. current + * time in millisecs) + * @param dest_mbrs The list of members who should receive the call. Usually a group RPC + * is sent via multicast, but a receiver drops the request if its own address + * is not in this list. Will not be used if it is null. + * @param msg The request to be sent. The body of the message carries + * the request data + * + * @param coll A response collector (usually the object that invokes + * this method). Its methods receiveResponse() and + * suspect() will be invoked when a message has been received + * or a member is suspected, respectively. + */ + public void sendRequest(long id, List
dest_mbrs, Message msg, RspCollector coll, boolean use_anycasting) throws Exception { + Header hdr; + + if(transport == null) { + if(log.isWarnEnabled()) log.warn("transport is not available !"); + return; + } + + // i. Create the request correlator header and add it to the + // msg + // ii. If a reply is expected (sync call / 'coll != null'), add a + // coresponding entry in the pending requests table + // iii. If deadlock detection is enabled, set/update the call stack + // iv. Pass the msg down to the protocol layer below + hdr=new Header(Header.REQ, id, (coll != null), name); + hdr.dest_mbrs=dest_mbrs; + + if (coll != null) { + if(deadlock_detection) { + if(local_addr == null) { + if(log.isErrorEnabled()) log.error("local address is null !"); + return; + } + java.util.Stack
new_call_stack = (call_stack != null? + (java.util.Stack
)call_stack.clone():new java.util.Stack
()); + new_call_stack.push(local_addr); + hdr.callStack=new_call_stack; + } + addEntry(hdr.id, coll); + } + msg.putHeader(name, hdr); + + if(transport instanceof Protocol) { + if(use_anycasting) { + Message copy; + for(Iterator it=dest_mbrs.iterator(); it.hasNext();) { + Address mbr=(Address)it.next(); + copy=msg.copy(true); + copy.setDest(mbr); + ((Protocol)transport).down(new Event(Event.MSG, copy)); + } + } + else { + ((Protocol)transport).down(new Event(Event.MSG, msg)); + } + } + else if(transport instanceof Transport) { + if(use_anycasting) { + Message copy; + for(Iterator it=dest_mbrs.iterator(); it.hasNext();) { + Address mbr=(Address)it.next(); + copy=msg.copy(true); + copy.setDest(mbr); + ((Transport)transport).send(copy); + } + } + else { + ((Transport)transport).send(msg); + } + } + else + throw new IllegalStateException("transport has to be either a Transport or a Protocol, however it is a " + transport.getClass()); + } + + + + + + /** + * Used to signal that a certain request may be garbage collected as + * all responses have been received. + */ + public void done(long id) { + removeEntry(id); + } + + + /** + * Callback. + *

+ * Called by the protocol below when a message has been received. The + * algorithm should test whether the message is destined for us and, + * if not, pass it up to the next layer. Otherwise, it should remove + * the header and check whether the message is a request or response. + * In the first case, the message will be delivered to the request + * handler registered (calling its handle() method), in the + * second case, the corresponding response collector is looked up and + * the message delivered. + * @param evt The event to be received + * @return Whether or not the event was consumed. If true, don't pass message up, else pass it up + */ + public boolean receive(Event evt) { + switch(evt.getType()) { + + case Event.SUSPECT: // don't wait for responses from faulty members + receiveSuspect((Address)evt.getArg()); + break; + + case Event.VIEW_CHANGE: // adjust number of responses to wait for + receiveView((View)evt.getArg()); + break; + + case Event.SET_LOCAL_ADDRESS: + setLocalAddress((Address)evt.getArg()); + break; + + case Event.MSG: + if(receiveMessage((Message)evt.getArg())) + return true; // message was consumed, don't pass it up + break; + } +// if(transport instanceof Protocol) +// ((Protocol)transport).getUpProtocol().up(evt); +// else +// if(log.isErrorEnabled()) log.error("we do not pass up messages via Transport"); + return false; + } + + + /** + */ + public final void start() { + if(deadlock_detection) { + startScheduler(); + } + started=true; + } + + public void stop() { + stopScheduler(); + started=false; + } + + + void startScheduler() { + if(scheduler == null) { + scheduler=new Scheduler(); + if(deadlock_detection && call_stack_setter == null) { + call_stack_setter=new CallStackSetter(); + scheduler.setListener(call_stack_setter); + } + if(concurrent_processing) + scheduler.setConcurrentProcessing(concurrent_processing); + scheduler.start(); + } + } + + + void stopScheduler() { + if(scheduler != null) { + scheduler.stop(); + scheduler=null; + } + } + + public void registerProbeHandler(TP transport) { + if(transport != null) + transport.registerProbeHandler(probe_handler); + } + + public void unregisterProbeHandler(TP transport) { + if(transport != null) + transport.unregisterProbeHandler(probe_handler); + } + + // ....................................................................... + + + + /** + * Event.SUSPECT event received from a layer below. + *

+ * All response collectors currently registered will + * be notified that mbr may have crashed, so they won't + * wait for its response. + */ + public void receiveSuspect(Address mbr) { + if(mbr == null) return; + if(log.isDebugEnabled()) log.debug("suspect=" + mbr); + + // copy so we don't run into bug #761804 - Bela June 27 2003 + // copy=new ArrayList(requests.values()); // removed because ConcurrentReaderHashMap can tolerate concurrent mods (bela May 8 2006) + for(RspCollector coll: requests.values()) { + if(coll != null) + coll.suspect(mbr); + } + } + + + /** + * Event.VIEW_CHANGE event received from a layer below. + *

+ * Mark all responses from members that are not in new_view as + * NOT_RECEIVED. + * + */ + public void receiveView(View new_view) { + // ArrayList copy; + // copy so we don't run into bug #761804 - Bela June 27 2003 + // copy=new ArrayList(requests.values()); // removed because ConcurrentReaderHashMap can tolerate concurrent mods (bela May 8 2006) + for(RspCollector coll: requests.values()) { + if(coll != null) + coll.viewChange(new_view); + } + } + + + /** + * Handles a message coming from a layer below + * + * @return true if the message was consumed, don't pass it further up, else false + */ + public boolean receiveMessage(Message msg) { + + // i. If header is not an instance of request correlator header, ignore + // + // ii. Check whether the message was sent by a request correlator with + // the same name (there may be multiple request correlators in the same + // protocol stack...) + Header hdr=(Header)msg.getHeader(name); + if(hdr == null) + return false; + + if(hdr.corrName == null || !hdr.corrName.equals(name)) { + if(log.isTraceEnabled()) { + log.trace(new StringBuilder("name of request correlator header (").append(hdr.corrName). + append(") is different from ours (").append(name).append("). Msg not accepted, passed up")); + } + return false; + } + + // If the header contains a destination list, and we are not part of it, then we discard the + // request (was addressed to other members) + java.util.List dests=hdr.dest_mbrs; + if(dests != null && local_addr != null && !dests.contains(local_addr)) { + if(log.isTraceEnabled()) { + log.trace(new StringBuilder("discarded request from ").append(msg.getSrc()). + append(" as we are not part of destination list (local_addr="). + append(local_addr).append(", hdr=").append(hdr).append(')')); + } + return true; // don't pass this message further up + } + + + // [Header.REQ]: + // i. If there is no request handler, discard + // ii. Check whether priority: if synchronous and call stack contains + // address that equals local address -> add priority request. Else + // add normal request. + // + // [Header.RSP]: + // Remove the msg request correlator header and notify the associated + // RspCollector that a reply has been received + switch(hdr.type) { + case Header.REQ: + if(request_handler == null) { + if(log.isWarnEnabled()) { + log.warn("there is no request handler installed to deliver request !"); + } + return true; + } + + if(deadlock_detection) { + if(scheduler == null) { + log.error("deadlock_detection is true, but scheduler is null: this is not supposed to happen" + + " (discarding request)"); + break; + } + + Request req=new Request(msg, hdr); + java.util.Stack stack=hdr.callStack; + if(hdr.rsp_expected && stack != null && local_addr != null) { + if(stack.contains(local_addr)) { + if(log.isTraceEnabled()) + log.trace("call stack=" + hdr.callStack + " contains " + local_addr + + ": adding request to priority queue"); + scheduler.addPrio(req); + break; + } + } + scheduler.add(req); + break; + } + + handleRequest(msg, hdr); + break; + + case Header.RSP: + msg.getHeader(name); + RspCollector coll=requests.get(Long.valueOf(hdr.id)); + if(coll != null) { + Address sender=msg.getSrc(); + Object retval=null; + byte[] buf=msg.getBuffer(); + int offset=msg.getOffset(), length=msg.getLength(); + try { + retval=marshaller != null? marshaller.objectFromByteBuffer(buf, offset, length) : + Util.objectFromByteBuffer(buf, offset, length); + } + catch(Exception e) { + log.error("failed unmarshalling buffer into return value", e); + retval=e; + } + coll.receiveResponse(retval, sender); + } + break; + + default: + msg.getHeader(name); + if(log.isErrorEnabled()) log.error("header's type is neither REQ nor RSP !"); + break; + } + + return true; // message was consumed + } + + public Address getLocalAddress() { + return local_addr; + } + + public void setLocalAddress(Address local_addr) { + this.local_addr=local_addr; + } + + + // ....................................................................... + + /** + * Add an association of:
+ * ID -> RspCollector + */ + private void addEntry(long id, RspCollector coll) { + requests.putIfAbsent(id, coll); + } + + + /** + * Remove the request entry associated with the given ID + * + * @param id the id of the RequestEntry to remove + */ + private void removeEntry(long id) { + Long id_obj = new Long(id); + + // changed by bela Feb 28 2003 (bug fix for 690606) + // changed back to use synchronization by bela June 27 2003 (bug fix for #761804), + // we can do this because we now copy for iteration (viewChange() and suspect()) + requests.remove(id_obj); + } + + + + /** + * Handle a request msg for this correlator + * + * @param req the request msg + */ + protected void handleRequest(Message req, Header hdr) { + Object retval; + Object rsp_buf; // either byte[] or Buffer + Header rsp_hdr; + Message rsp; + + // i. Get the request correlator header from the msg and pass it to + // the registered handler + // + // ii. If a reply is expected, pack the return value from the request + // handler to a reply msg and send it back. The reply msg has the same + // ID as the request and the name of the sender request correlator + + if(log.isTraceEnabled()) { + log.trace(new StringBuilder("calling (").append((request_handler != null? request_handler.getClass().getName() : "null")). + append(") with request ").append(hdr.id)); + } + + try { + retval=request_handler.handle(req); + } + catch(Throwable t) { + if(log.isErrorEnabled()) log.error("error invoking method", t); + retval=t; + } + + if(!hdr.rsp_expected) // asynchronous call, we don't need to send a response; terminate call here + return; + + if(transport == null) { + if(log.isErrorEnabled()) log.error("failure sending response; no transport available"); + return; + } + + // changed (bela Feb 20 2004): catch exception and return exception + try { // retval could be an exception, or a real value + rsp_buf=marshaller != null? marshaller.objectToBuffer(retval) : Util.objectToByteBuffer(retval); + } + catch(Throwable t) { + try { // this call should succeed (all exceptions are serializable) + rsp_buf=marshaller != null? marshaller.objectToBuffer(t) : Util.objectToByteBuffer(t); + } + catch(Throwable tt) { + if(log.isErrorEnabled()) log.error("failed sending rsp: return value (" + retval + ") is not serializable"); + return; + } + } + + rsp=req.makeReply(); + rsp.setFlag(Message.OOB); // back ported from 2.7: make response OOB no matter what + if(rsp_buf instanceof Buffer) + rsp.setBuffer((Buffer)rsp_buf); + else if (rsp_buf instanceof byte[]) + rsp.setBuffer((byte[])rsp_buf); + rsp_hdr=new Header(Header.RSP, hdr.id, false, name); + rsp.putHeader(name, rsp_hdr); + if(log.isTraceEnabled()) + log.trace(new StringBuilder("sending rsp for ").append(rsp_hdr.id).append(" to ").append(rsp.getDest())); + + try { + if(transport instanceof Protocol) + ((Protocol)transport).down(new Event(Event.MSG, rsp)); + else if(transport instanceof Transport) + ((Transport)transport).send(rsp); + else + if(log.isErrorEnabled()) log.error("transport object has to be either a " + + "Transport or a Protocol, however it is a " + transport.getClass()); + } + catch(Throwable e) { + if(log.isErrorEnabled()) log.error("failed sending the response", e); + } + } + + + // ....................................................................... + + + + + + /** + * The header for RequestCorrelator messages + */ + public static final class Header extends org.jgroups.Header implements Streamable { + public static final byte REQ = 0; + public static final byte RSP = 1; + + /** Type of header: request or reply */ + public byte type=REQ; + /** + * The id of this request to distinguish among other requests from + * the same RequestCorrelator */ + public long id=0; + + /** msg is synchronous if true */ + public boolean rsp_expected=true; + + /** The unique name of the associated RequestCorrelator */ + public String corrName=null; + + /** Stack<Address>. Contains senders (e.g. P --> Q --> R) */ + public java.util.Stack

callStack=null; + + /** Contains a list of members who should receive the request (others will drop). Ignored if null */ + public java.util.List
dest_mbrs=null; + + + /** + * Used for externalization + */ + public Header() {} + + /** + * @param type type of header (REQ/RSP) + * @param id id of this header relative to ids of other requests + * originating from the same correlator + * @param rsp_expected whether it's a sync or async request + * @param name the name of the RequestCorrelator from which + */ + public Header(byte type, long id, boolean rsp_expected, String name) { + this.type = type; + this.id = id; + this.rsp_expected = rsp_expected; + this.corrName = name; + } + + /** + */ + public String toString() { + StringBuilder ret=new StringBuilder(); + ret.append("[Header: name=" + corrName + ", type="); + ret.append(type == REQ ? "REQ" : type == RSP ? "RSP" : ""); + ret.append(", id=" + id); + ret.append(", rsp_expected=" + rsp_expected + ']'); + if(callStack != null) + ret.append(", call stack=" + callStack); + if(dest_mbrs != null) + ret.append(", dest_mbrs=").append(dest_mbrs); + return ret.toString(); + } + + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeByte(type); + out.writeLong(id); + out.writeBoolean(rsp_expected); + if(corrName != null) { + out.writeBoolean(true); + out.writeUTF(corrName); + } + else { + out.writeBoolean(false); + } + out.writeObject(callStack); + out.writeObject(dest_mbrs); + } + + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + type = in.readByte(); + id = in.readLong(); + rsp_expected = in.readBoolean(); + if(in.readBoolean()) + corrName = in.readUTF(); + callStack = (java.util.Stack
)in.readObject(); + dest_mbrs=(java.util.List
)in.readObject(); + } + + public void writeTo(DataOutputStream out) throws IOException { + out.writeByte(type); + out.writeLong(id); + out.writeBoolean(rsp_expected); + + if(corrName != null) { + out.writeBoolean(true); + out.writeUTF(corrName); + } + else { + out.writeBoolean(false); + } + + if(callStack != null) { + out.writeBoolean(true); + out.writeShort(callStack.size()); + Address mbr; + for(int i=0; i < callStack.size(); i++) { + mbr=(Address)callStack.elementAt(i); + Util.writeAddress(mbr, out); + } + } + else { + out.writeBoolean(false); + } + + Util.writeAddresses(dest_mbrs, out); + } + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + boolean present; + type=in.readByte(); + id=in.readLong(); + rsp_expected=in.readBoolean(); + + present=in.readBoolean(); + if(present) + corrName=in.readUTF(); + + present=in.readBoolean(); + if(present) { + callStack=new Stack
(); + short len=in.readShort(); + Address tmp; + for(short i=0; i < len; i++) { + tmp=Util.readAddress(in); + callStack.add(tmp); + } + } + + dest_mbrs=(List
)Util.readAddresses(in, java.util.LinkedList.class); + } + + public int size() { + int retval=Global.BYTE_SIZE // type + + Global.LONG_SIZE // id + + Global.BYTE_SIZE; // rsp_expected + + retval+=Global.BYTE_SIZE; // presence for corrName + if(corrName != null) + retval+=corrName.length() +2; // UTF + + retval+=Global.BYTE_SIZE; // presence + if(callStack != null) { + retval+=Global.SHORT_SIZE; // number of elements + if(!callStack.isEmpty()) { + Address mbr=(Address)callStack.firstElement(); + retval+=callStack.size() * (Util.size(mbr)); + } + } + + retval+=Util.size(dest_mbrs); + return retval; + } + } + + + private static class MyProbeHandler implements TP.ProbeHandler { + private final ConcurrentMap requests; + + public MyProbeHandler(ConcurrentMap requests) { + this.requests=requests; + } + + public Map handleProbe(String... keys) { + if(requests == null) + return null; + Map retval=new HashMap(); + for(String key: keys) { + if(key.equals("requests")) { + StringBuilder sb=new StringBuilder(); + for(Map.Entry entry: requests.entrySet()) { + sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); + } + retval.put("requests", sb.toString()); + break; + } + } + return retval; + } + + public String[] supportedKeys() { + return new String[]{"requests"}; + } + } + + + /** + * Listens for scheduler events and sets the current call chain (stack) + * whenever a thread is started, or a suspended thread resumed. Does + * this only for synchronous requests (Runnable is actually + * a Request). + */ + private class CallStackSetter implements SchedulerListener { + public void started(Runnable r) { setCallStack(r); } + public void stopped(Runnable r) { setCallStack(null); } + public void suspended(Runnable r) { setCallStack(null); } + public void resumed(Runnable r) { setCallStack(r); } + + void setCallStack(Runnable r) { + java.util.Stack new_stack; + Message req; + Header hdr; + Object obj; + + if(r == null) { + call_stack=null; + return; + } + + req=((Request)r).req; + if(req == null) + return; + + obj=req.getHeader(name); + if(obj == null || !(obj instanceof Header)) + return; + + hdr=(Header)obj; + if(hdr.rsp_expected == false) + return; + + new_stack=hdr.callStack; + if(new_stack != null) + call_stack=(java.util.Stack
)new_stack.clone(); + } + } + + + /** + * The runnable for an incoming request which is submitted to the + * dispatcher + */ + private class Request implements Runnable { + final Message req; + final Header hdr; + + public Request(Message req, Header hdr) { this.req=req; this.hdr=hdr;} + public void run() { handleRequest(req, hdr); } + + public String toString() { + StringBuilder sb=new StringBuilder(); + if(req != null) + sb.append("req=" + req + ", headers=" + req.printObjectHeaders()); + return sb.toString(); + } + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/blocks/RequestHandler.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/blocks/RequestHandler.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/blocks/RequestHandler.java 17 Aug 2012 14:51:18 -0000 1.1 @@ -0,0 +1,11 @@ +// $Id: RequestHandler.java,v 1.1 2012/08/17 14:51:18 marcin Exp $ + +package org.jgroups.blocks; + + +import org.jgroups.Message; + + +public interface RequestHandler { + Object handle(Message msg); +} Index: 3rdParty_sources/jgroups/org/jgroups/blocks/RpcDispatcher.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/blocks/RpcDispatcher.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/blocks/RpcDispatcher.java 17 Aug 2012 14:51:18 -0000 1.1 @@ -0,0 +1,510 @@ +// $Id: RpcDispatcher.java,v 1.1 2012/08/17 14:51:18 marcin Exp $ + +package org.jgroups.blocks; + + +import org.jgroups.*; +import org.jgroups.util.RspList; +import org.jgroups.util.Util; +import org.jgroups.util.Buffer; + +import java.io.Serializable; +import java.lang.reflect.Method; +import java.lang.IllegalArgumentException ; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Vector; + + + + +/** + * This class allows a programmer to invoke remote methods in all (or single) + * group members and optionally wait for the return value(s). + * An application will typically create a channel and layer the + * RpcDispatcher building block on top of it, which allows it to + * dispatch remote methods (client role) and at the same time be + * called by other members (server role). + * This class is derived from MessageDispatcher. +* Is the equivalent of RpcProtocol on the application rather than protocol level. + * @author Bela Ban + */ +public class RpcDispatcher extends MessageDispatcher implements ChannelListener { + protected Object server_obj=null; + /** Marshaller to marshall requests at the caller and unmarshal requests at the receiver(s) */ + protected Marshaller2 req_marshaller=null; + + /** Marshaller to marshal responses at the receiver(s) and unmarshal responses at the caller */ + protected Marshaller2 rsp_marshaller=null; + protected final List additionalChannelListeners=new ArrayList(); + protected MethodLookup method_lookup=null; + + + public RpcDispatcher() { + } + + + public RpcDispatcher(Channel channel, MessageListener l, MembershipListener l2, Object server_obj) { + super(channel, l, l2); + channel.addChannelListener(this); + this.server_obj=server_obj; + } + + + public RpcDispatcher(Channel channel, MessageListener l, MembershipListener l2, Object server_obj, + boolean deadlock_detection) { + super(channel, l, l2, deadlock_detection); + channel.addChannelListener(this); + this.server_obj=server_obj; + } + + public RpcDispatcher(Channel channel, MessageListener l, MembershipListener l2, Object server_obj, + boolean deadlock_detection, boolean concurrent_processing) { + super(channel, l, l2, deadlock_detection, concurrent_processing); + channel.addChannelListener(this); + this.server_obj=server_obj; + } + + + + public RpcDispatcher(PullPushAdapter adapter, Serializable id, + MessageListener l, MembershipListener l2, Object server_obj) { + super(adapter, id, l, l2); + + // Fixes bug #804956 + // channel.setChannelListener(this); + if(this.adapter != null) { + Transport t=this.adapter.getTransport(); + if(t != null && t instanceof Channel) { + ((Channel)t).addChannelListener(this); + } + } + + this.server_obj=server_obj; + } + + + public interface Marshaller { + byte[] objectToByteBuffer(Object obj) throws Exception; + Object objectFromByteBuffer(byte[] buf) throws Exception; + } + + + public interface Marshaller2 extends Marshaller { + /** + * Marshals the object into a byte[] buffer and returns a Buffer with a ref to the underlying byte[] buffer, + * offset and length.
+ * + * Note that the underlying byte[] buffer must not be changed as this would change the buffer of a message which + * potentially can get retransmitted, and such a retransmission would then carry a ref to a changed byte[] buffer ! + * + * @param obj + * @return + * @throws Exception + */ + Buffer objectToBuffer(Object obj) throws Exception; + + Object objectFromByteBuffer(byte[] buf, int offset, int length) throws Exception; + } + + + /** Used to provide a Marshaller2 interface to a Marshaller. This class is for internal use only, and will be + * removed in 3.0 when Marshaller and Marshaller2 get merged. Do not use, but provide an implementation of + * Marshaller directly, e.g. in setRequestMarshaller(). + */ + public static class MarshallerAdapter implements Marshaller2 { + private final Marshaller marshaller; + + public MarshallerAdapter(Marshaller marshaller) { + this.marshaller=marshaller; + } + + public byte[] objectToByteBuffer(Object obj) throws Exception { + return marshaller.objectToByteBuffer(obj); + } + + public Object objectFromByteBuffer(byte[] buf) throws Exception { + return buf == null? null : marshaller.objectFromByteBuffer(buf); + } + + public Buffer objectToBuffer(Object obj) throws Exception { + byte[] buf=marshaller.objectToByteBuffer(obj); + return new Buffer(buf, 0, buf.length); + } + + public Object objectFromByteBuffer(byte[] buf, int offset, int length) throws Exception { + if(buf == null || (offset == 0 && length == buf.length)) + return marshaller.objectFromByteBuffer(buf); + byte[] tmp=new byte[length]; + System.arraycopy(buf, offset, tmp, 0, length); + return marshaller.objectFromByteBuffer(tmp); + } + + } + + + public String getName() {return "RpcDispatcher";} + + public Marshaller getRequestMarshaller() {return req_marshaller;} + + public void setRequestMarshaller(Marshaller m) { + if(m == null) + this.req_marshaller=null; + else if(m instanceof Marshaller2) + this.req_marshaller=(Marshaller2)m; + else + this.req_marshaller=new MarshallerAdapter(m); + } + + public Marshaller getResponseMarshaller() {return rsp_marshaller;} + + public void setResponseMarshaller(Marshaller m) { + if(m == null) + this.rsp_marshaller=null; + else if(m instanceof Marshaller2) + this.rsp_marshaller=(Marshaller2)m; + else + this.rsp_marshaller=new MarshallerAdapter(m); + + if(corr != null) + corr.setMarshaller(this.rsp_marshaller); + } + + public Marshaller getMarshaller() {return req_marshaller;} + + public void setMarshaller(Marshaller m) {setRequestMarshaller(m);} + + public Object getServerObject() {return server_obj;} + + public void setServerObject(Object server_obj) { + this.server_obj=server_obj; + } + + public MethodLookup getMethodLookup() { + return method_lookup; + } + + public void setMethodLookup(MethodLookup method_lookup) { + this.method_lookup=method_lookup; + } + + + public RspList castMessage(Vector dests, Message msg, int mode, long timeout) { + if(log.isErrorEnabled()) log.error("this method should not be used with " + + "RpcDispatcher, but MessageDispatcher. Returning null"); + return null; + } + + public Object sendMessage(Message msg, int mode, long timeout) throws TimeoutException, SuspectedException { + if(log.isErrorEnabled()) log.error("this method should not be used with " + + "RpcDispatcher, but MessageDispatcher. Returning null"); + return null; + } + + + public RspList callRemoteMethods(Vector dests, String method_name, Object[] args, + Class[] types, int mode, long timeout) { + return callRemoteMethods(dests, method_name, args, types, mode, timeout, false); + } + + + + public RspList callRemoteMethods(Vector dests, String method_name, Object[] args, + Class[] types, int mode, long timeout, boolean use_anycasting) { + return callRemoteMethods(dests, method_name, args, types, mode, timeout, use_anycasting, null); + } + + public RspList callRemoteMethods(Vector dests, String method_name, Object[] args, + Class[] types, int mode, long timeout, boolean use_anycasting, RspFilter filter) { + MethodCall method_call=new MethodCall(method_name, args, types); + return callRemoteMethods(dests, method_call, mode, timeout, use_anycasting, false, filter); + } + + + public RspList callRemoteMethods(Vector dests, String method_name, Object[] args, + String[] signature, int mode, long timeout) { + return callRemoteMethods(dests, method_name, args, signature, mode, timeout, false); + } + + + public RspList callRemoteMethods(Vector dests, String method_name, Object[] args, + String[] signature, int mode, long timeout, boolean use_anycasting) { + MethodCall method_call=new MethodCall(method_name, args, signature); + return callRemoteMethods(dests, method_call, mode, timeout, use_anycasting); + } + + + public RspList callRemoteMethods(Vector dests, MethodCall method_call, int mode, long timeout) { + return callRemoteMethods(dests, method_call, mode, timeout, false); + } + + public RspList callRemoteMethods(Vector dests, MethodCall method_call, int mode, long timeout, boolean use_anycasting) { + return callRemoteMethods(dests, method_call, mode, timeout, use_anycasting, false); + } + + public RspList callRemoteMethods(Vector dests, MethodCall method_call, int mode, long timeout, + boolean use_anycasting, boolean oob) { + return callRemoteMethods(dests, method_call, mode, timeout, use_anycasting, oob, null); + } + + + public RspList callRemoteMethods(Vector dests, MethodCall method_call, int mode, long timeout, + boolean use_anycasting, boolean oob, RspFilter filter) { + if(dests != null && dests.isEmpty()) { + // don't send if dest list is empty + if(log.isTraceEnabled()) + log.trace(new StringBuilder("destination list of ").append(method_call.getName()). + append("() is empty: no need to send message")); + return new RspList(); + } + + if(log.isTraceEnabled()) + log.trace(new StringBuilder("dests=").append(dests).append(", method_call=").append(method_call). + append(", mode=").append(mode).append(", timeout=").append(timeout)); + + Object buf; + try { + buf=req_marshaller != null? req_marshaller.objectToBuffer(method_call) : Util.objectToByteBuffer(method_call); + } + catch(Exception e) { + // if(log.isErrorEnabled()) log.error("exception", e); + // we will change this in 2.4 to add the exception to the signature + // (see http://jira.jboss.com/jira/browse/JGRP-193). The reason for a RTE is that we cannot change the + // signature in 2.3, otherwise 2.3 would be *not* API compatible to prev releases + throw new RuntimeException("failure to marshal argument(s)", e); + } + + Message msg=new Message(); + if(buf instanceof Buffer) + msg.setBuffer((Buffer)buf); + else + msg.setBuffer((byte[])buf); + if(oob) + msg.setFlag(Message.OOB); + RspList retval=super.castMessage(dests, msg, mode, timeout, use_anycasting, filter); + if(log.isTraceEnabled()) log.trace("responses: " + retval); + return retval; + } + + + public Object callRemoteMethod(Address dest, String method_name, Object[] args, + Class[] types, int mode, long timeout) throws Throwable { + MethodCall method_call=new MethodCall(method_name, args, types); + return callRemoteMethod(dest, method_call, mode, timeout); + } + + public Object callRemoteMethod(Address dest, String method_name, Object[] args, + String[] signature, int mode, long timeout) throws Throwable { + MethodCall method_call=new MethodCall(method_name, args, signature); + return callRemoteMethod(dest, method_call, mode, timeout); + } + + public Object callRemoteMethod(Address dest, MethodCall method_call, int mode, long timeout) throws Throwable { + return callRemoteMethod(dest, method_call, mode, timeout, false); + } + + public Object callRemoteMethod(Address dest, MethodCall method_call, int mode, long timeout, boolean oob) throws Throwable { + Object buf=null; + Message msg=null; + Object retval=null; + + if(log.isTraceEnabled()) + log.trace("dest=" + dest + ", method_call=" + method_call + ", mode=" + mode + ", timeout=" + timeout); + + buf=req_marshaller != null? req_marshaller.objectToBuffer(method_call) : Util.objectToByteBuffer(method_call); + msg=new Message(dest, null, null); + if(buf instanceof Buffer) + msg.setBuffer((Buffer)buf); + else + msg.setBuffer((byte[])buf); + if(oob) + msg.setFlag(Message.OOB); + retval=super.sendMessage(msg, mode, timeout); + if(log.isTraceEnabled()) log.trace("retval: " + retval); + if(retval instanceof Throwable) + throw (Throwable)retval; + return retval; + } + + + protected void correlatorStarted() { + if(corr != null) + corr.setMarshaller(rsp_marshaller); + } + + + /** + * Message contains MethodCall. Execute it against *this* object and return result. + * Use MethodCall.invoke() to do this. Return result. + */ + public Object handle(Message req) { + Object body=null; + MethodCall method_call; + + if(server_obj == null) { + if(log.isErrorEnabled()) log.error("no method handler is registered. Discarding request."); + return null; + } + + if(req == null || req.getLength() == 0) { + if(log.isErrorEnabled()) log.error("message or message buffer is null"); + return null; + } + + try { + body=req_marshaller != null? + req_marshaller.objectFromByteBuffer(req.getBuffer(), req.getOffset(), req.getLength()) + : req.getObject(); + } + catch(Throwable e) { + if(log.isErrorEnabled()) log.error("exception marshalling object", e); + return e; + } + + if(!(body instanceof MethodCall)) { + if(log.isErrorEnabled()) log.error("message does not contain a MethodCall object"); + + // create an exception to represent this and return it + return new IllegalArgumentException("message does not contain a MethodCall object") ; + } + + method_call=(MethodCall)body; + + try { + if(log.isTraceEnabled()) + log.trace("[sender=" + req.getSrc() + "], method_call: " + method_call); + + if(method_call.getMode() == MethodCall.ID) { + if(method_lookup == null) + throw new Exception("MethodCall uses ID=" + method_call.getId() + ", but method_lookup has not been set"); + Method m=method_lookup.findMethod(method_call.getId()); + if(m == null) + throw new Exception("no method foudn for " + method_call.getId()); + method_call.setMethod(m); + } + + return method_call.invoke(server_obj); + } + catch(Throwable x) { + return x; + } + } + + /** + * Add a new channel listener to be notified on the channel's state change. + * + * @return true if the listener was added or false if the listener was already in the list. + */ + public boolean addChannelListener(ChannelListener l) { + + synchronized(additionalChannelListeners) { + if (additionalChannelListeners.contains(l)) { + return false; + } + additionalChannelListeners.add(l); + return true; + } + } + + + /** + * + * @return true if the channel was removed indeed. + */ + public boolean removeChannelListener(ChannelListener l) { + + synchronized(additionalChannelListeners) { + return additionalChannelListeners.remove(l); + } + } + + + + /* --------------------- Interface ChannelListener ---------------------- */ + + public void channelConnected(Channel channel) { + + synchronized(additionalChannelListeners) { + for(Iterator i = additionalChannelListeners.iterator(); i.hasNext(); ) { + ChannelListener l = (ChannelListener)i.next(); + try { + l.channelConnected(channel); + } + catch(Throwable t) { + log.warn("channel listener failed", t); + } + } + } + } + + public void channelDisconnected(Channel channel) { + + stop(); + + synchronized(additionalChannelListeners) { + for(Iterator i = additionalChannelListeners.iterator(); i.hasNext(); ) { + ChannelListener l = (ChannelListener)i.next(); + try { + l.channelDisconnected(channel); + } + catch(Throwable t) { + log.warn("channel listener failed", t); + } + } + } + } + + public void channelClosed(Channel channel) { + + stop(); + + synchronized(additionalChannelListeners) { + for(Iterator i = additionalChannelListeners.iterator(); i.hasNext(); ) { + ChannelListener l = (ChannelListener)i.next(); + try { + l.channelClosed(channel); + } + catch(Throwable t) { + log.warn("channel listener failed", t); + } + } + } + } + + public void channelShunned() { + + synchronized(additionalChannelListeners) { + for(Iterator i = additionalChannelListeners.iterator(); i.hasNext(); ) { + ChannelListener l = (ChannelListener)i.next(); + try { + l.channelShunned(); + } + catch(Throwable t) { + log.warn("channel listener failed", t); + } + } + } + } + + public void channelReconnected(Address new_addr) { + if(log.isTraceEnabled()) + log.trace("channel has been rejoined, old local_addr=" + local_addr + ", new local_addr=" + new_addr); + this.local_addr=new_addr; + start(); + + synchronized(additionalChannelListeners) { + for(Iterator i = additionalChannelListeners.iterator(); i.hasNext(); ) { + ChannelListener l = (ChannelListener)i.next(); + try { + l.channelReconnected(new_addr); + } + catch(Throwable t) { + log.warn("channel listener failed", t); + } + } + } + } + /* ----------------------------------------------------------------------- */ + +} Index: 3rdParty_sources/jgroups/org/jgroups/blocks/RspCollector.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/blocks/RspCollector.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/blocks/RspCollector.java 17 Aug 2012 14:51:17 -0000 1.1 @@ -0,0 +1,14 @@ +// $Id: RspCollector.java,v 1.1 2012/08/17 14:51:17 marcin Exp $ + +package org.jgroups.blocks; + +import org.jgroups.Address; +import org.jgroups.Message; +import org.jgroups.View; + + +public interface RspCollector { + void receiveResponse(Object response_value, Address sender); + void suspect(Address mbr); + void viewChange(View new_view); +} Index: 3rdParty_sources/jgroups/org/jgroups/blocks/RspFilter.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/blocks/RspFilter.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/blocks/RspFilter.java 17 Aug 2012 14:51:17 -0000 1.1 @@ -0,0 +1,31 @@ +package org.jgroups.blocks; + +import org.jgroups.Address; + +/** + * Interface defining when a group request is done. This allows for termination of a group request based on + * logic implemented by the caller. Example: caller uses mode GET_FIRST plus a RspFilter implementation. Here, the + * request will not return (assuming timeout is 0) when the first response has been received, but when the filter + * passed + * @author Bela Ban + * @version $Id: RspFilter.java,v 1.1 2012/08/17 14:51:17 marcin Exp $ + */ +public interface RspFilter { + + + /** + * Determines whether a response from a given sender should be added to the response list of the request + * @param response The response (usually a serializable value) + * @param sender The sender of response + * @return True if we should add the response to the response list ({@link org.jgroups.util.RspList}) of a request, + * otherwise false. In the latter case, we don't add the response to the response list. + */ + boolean isAcceptable(Object response, Address sender); + + /** + * Right after calling {@link #isAcceptable(Object, org.jgroups.Address)}, this method is called to see whether + * we are done with the request and can unblock the caller + * @return False if the request is done, otherwise true + */ + boolean needMoreResponses(); +} Index: 3rdParty_sources/jgroups/org/jgroups/blocks/TwoPhaseVotingAdapter.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/blocks/TwoPhaseVotingAdapter.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/blocks/TwoPhaseVotingAdapter.java 17 Aug 2012 14:51:18 -0000 1.1 @@ -0,0 +1,165 @@ +package org.jgroups.blocks; + +import org.jgroups.ChannelException; + +/** + * This adapter introduces simple two-phase voting on a specified decree. All + * nodes in the group receive a decree in "prepare" phase where they expres + * their opinion on the decree. If all nodes voted positively on decree, next + * phase "commit" fixes changes that were made in "prepare" phase, otherwise + * changes are canceled in "abort" phase. + * + * @author Roman Rokytskyy (rrokytskyy@acm.org) + * @author Robert Schaffar-Taurok (robert@fusion.at) + * @version $Id: TwoPhaseVotingAdapter.java,v 1.1 2012/08/17 14:51:18 marcin Exp $ + */ +public class TwoPhaseVotingAdapter { + + private final VotingAdapter voteChannel; + + /** + * Creats an instance of the class. + * @param voteChannel the channel that will be used for voting. + */ + public TwoPhaseVotingAdapter(VotingAdapter voteChannel) { + this.voteChannel = voteChannel; + } + + /** + * Wraps actual listener with the VoteChannelListener and adds to the + * voteChannel + */ + public void addListener(TwoPhaseVotingListener listener) { + voteChannel.addVoteListener(new TwoPhaseVoteWrapper(listener)); + } + + /** + * Removes the listener from the voteChannel + */ + public void removeListener(TwoPhaseVotingListener listener) { + voteChannel.removeVoteListener(new TwoPhaseVoteWrapper(listener)); + } + + /** + * Performs the two-phase voting on the decree. After the voting each + * group member remains in the same state as others. + */ + public boolean vote(Object decree, long timeout) throws ChannelException { + return vote(decree, timeout, null); + } + + /** + * Performs the two-phase voting on the decree. After the voting each + * group member remains in the same state as others. + */ + public boolean vote(Object decree, long timeout, VoteResponseProcessor voteResponseProcessor) throws ChannelException { + // wrap real decree + TwoPhaseWrapper wrappedDecree = new TwoPhaseWrapper(decree); + + // check the decree acceptance + try { + if (voteChannel.vote(wrappedDecree, timeout / 3, voteResponseProcessor)) { + wrappedDecree.commit(); + + // try to commit decree + if (!voteChannel.vote(wrappedDecree, timeout / 3, voteResponseProcessor)) { + // strange, should fail during prepare... abort all + wrappedDecree.abort(); + voteChannel.vote(wrappedDecree, timeout / 3, voteResponseProcessor); + return false; + } else + return true; + + } else { + // somebody is not accepting the decree... abort + wrappedDecree.abort(); + voteChannel.vote(wrappedDecree, timeout / 3, voteResponseProcessor); + return false; + } + } catch(ChannelException chex) { + wrappedDecree.abort(); + voteChannel.vote(wrappedDecree, timeout / 3, voteResponseProcessor); + throw chex; + } + } + + + /** + * @return Returns the voteChannel. + */ + public VotingAdapter getVoteChannel() { + return voteChannel; + } + + public static class TwoPhaseVoteWrapper implements VotingListener { + + private final TwoPhaseVotingListener listener; + + public TwoPhaseVoteWrapper(TwoPhaseVotingListener listener) { + this.listener = listener; + } + + public boolean vote(Object decree) throws VoteException { + if (!(decree instanceof TwoPhaseWrapper)) + throw new VoteException("Not my type of decree. Ignore me."); + + TwoPhaseWrapper wrapper = (TwoPhaseWrapper)decree; + + // invoke the corresponding operation + if (wrapper.isPrepare()) + return listener.prepare(wrapper.getDecree()); + else + if (wrapper.isCommit()) + return listener.commit(wrapper.getDecree()); + else { + listener.abort(wrapper.getDecree()); + return false; + } + } + + /* + + This wrapper is completely equal to the object it wraps. + + Therefore the hashCode():int and equals(Object):boolean are + simply delegated to the wrapped code. + + */ + + public int hashCode() { return listener.hashCode(); } + public boolean equals(Object other) { return listener.equals(other); } + } + + /** + * Wrapper of the decree to voting decree. + */ + public static class TwoPhaseWrapper implements java.io.Serializable { + private static final int PREPARE = 0; + private static final int COMMIT = 1; + private static final int ABORT = 2; + + public TwoPhaseWrapper(Object decree) { + setDecree(decree); + setType(PREPARE); + } + + private Object decree; + private int type; + + public Object getDecree(){ return decree; } + public void setDecree(Object decree){ this.decree = decree; } + + private int getType() { return type; } + private void setType(int type) { this.type = type; } + private boolean isType(int type) { return this.type == type; } + + public boolean isPrepare() { return isType(PREPARE); } + public boolean isCommit() { return isType(COMMIT); } + public boolean isAbort() { return isType(ABORT); } + + public void commit() { setType(COMMIT); } + public void abort() { setType(ABORT); } + + public String toString() { return decree.toString(); } + } +} \ No newline at end of file Index: 3rdParty_sources/jgroups/org/jgroups/blocks/TwoPhaseVotingListener.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/blocks/TwoPhaseVotingListener.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/blocks/TwoPhaseVotingListener.java 17 Aug 2012 14:51:18 -0000 1.1 @@ -0,0 +1,31 @@ +package org.jgroups.blocks; + +/** + * Implementations of this interface can participate in two-phase voting process. + * + * @author Roman Rokytskyy (rrokytskyy@acm.org) + */ +public interface TwoPhaseVotingListener { + /** + * This is voting if the decree is acceptable to the party. + * @return true if the decree is acceptable. + * @throws VoteException if the decree type is unknown or listener + * does not want to vote on it. + */ + boolean prepare(Object decree) throws VoteException; + + /** + * This is voting on the commiting the decree. + * @return true is the decree is commited. + * @throws VoteException if the decree type is unknown or listener + * does not want to vote on it. + */ + boolean commit(Object decree) throws VoteException; + + /** + * This is unconditional abort of the previous voting on the decree. + * @throws VoteException if the listener ignores the abort. + */ + void abort(Object decree) throws VoteException; + +} \ No newline at end of file Index: 3rdParty_sources/jgroups/org/jgroups/blocks/UpdateException.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/blocks/UpdateException.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/blocks/UpdateException.java 17 Aug 2012 14:51:17 -0000 1.1 @@ -0,0 +1,16 @@ +// $Id: UpdateException.java,v 1.1 2012/08/17 14:51:17 marcin Exp $ + + +package org.jgroups.blocks; + + + +public class UpdateException extends Exception { + + private static final long serialVersionUID = -4196360091623991749L; + + public UpdateException(String msg) { + super(msg); + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/blocks/VoteException.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/blocks/VoteException.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/blocks/VoteException.java 17 Aug 2012 14:51:17 -0000 1.1 @@ -0,0 +1,16 @@ +package org.jgroups.blocks; + +import org.jgroups.ChannelException; + +/** + * This exception is thrown when voting listener cannot vote on the + * specified decree. + * + * @author Roman Rokytskyy (rrokytskyy@acm.org) + */ +public class VoteException extends ChannelException { + + private static final long serialVersionUID = -878345689312038489L; + + public VoteException(String msg) { super(msg); } +} \ No newline at end of file Index: 3rdParty_sources/jgroups/org/jgroups/blocks/VoteResponseProcessor.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/blocks/VoteResponseProcessor.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/blocks/VoteResponseProcessor.java 17 Aug 2012 14:51:18 -0000 1.1 @@ -0,0 +1,26 @@ +package org.jgroups.blocks; + +import org.jgroups.ChannelException; +import org.jgroups.util.RspList; + + +/** + * VoteResultProcessor + * Applications that use the VotingAdapter and/or TwoPhaseVotingAdapter can pass an implementation of this down the vote + * calls, to intercept processing of the VoteResults returned by other nodes. + * See the source of {@link org.jgroups.blocks.DistributedLockManager} for an example implementation. + * + * @author Robert Schaffar-Taurok (robert@fusion.at) + * @version $Id: VoteResponseProcessor.java,v 1.1 2012/08/17 14:51:18 marcin Exp $ + */ +public interface VoteResponseProcessor { + /** + * Processes the responses returned by the other nodes. + * @param responses The responses + * @param consensusType The consensusType of the vote + * @param decree The vote decree + * @return boolean + * @throws ChannelException + */ + public boolean processResponses(RspList responses, int consensusType, Object decree) throws ChannelException; +} Index: 3rdParty_sources/jgroups/org/jgroups/blocks/VotingAdapter.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/blocks/VotingAdapter.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/blocks/VotingAdapter.java 17 Aug 2012 14:51:18 -0000 1.1 @@ -0,0 +1,504 @@ +package org.jgroups.blocks; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.*; +import org.jgroups.util.Rsp; +import org.jgroups.util.RspList; + +import java.io.Serializable; +import java.util.*; + +/** + * Voting adapter provides a voting functionality for an application. There + * should be at most one {@link VotingAdapter} listening on one {@link Channel} + * instance. Each adapter can have zero or more registered {@link VotingListener} + * instances that will be called during voting process. + *

+ * Decree is an object that has some semantic meaning within the application. + * Each voting listener receives a decree and can respond with either + * true or false. If the decree has no meaning for the voting + * listener, it is required to throw {@link VoteException}. In this case + * this specific listener will be excluded from the voting on the specified + * decree. After performing local voting, this voting adapter sends the request + * back to the originator of the voting process. Originator receives results + * from each node and decides if all voting process succeeded or not depending + * on the consensus type specified during voting. + * + * @author Roman Rokytskyy (rrokytskyy@acm.org) + * @author Robert Schaffar-Taurok (robert@fusion.at) + * @version $Id: VotingAdapter.java,v 1.1 2012/08/17 14:51:18 marcin Exp $ + */ +public class VotingAdapter implements MessageListener, MembershipListener, VoteResponseProcessor { + + /** + * This consensus type means that at least one positive vote is required + * for the voting to succeed. + */ + public static final int VOTE_ANY = 0; + + /** + * This consensus type means that at least one positive vote and no negative + * votes are required for the voting to succeed. + */ + public static final int VOTE_ALL = 1; + + /** + * This consensus type means that number of positive votes should be greater + * than number of negative votes. + */ + public static final int VOTE_MAJORITY = 2; + + + private static final int PROCESS_CONTINUE = 0; + private static final int PROCESS_SKIP = 1; + private static final int PROCESS_BREAK = 2; + + + private final RpcDispatcher rpcDispatcher; + + protected final Log log=LogFactory.getLog(getClass()); + + private final HashSet suspectedNodes = new HashSet(); + private boolean closed; + + private final List membership_listeners=new LinkedList(); + + + + /** + * Creates an instance of the VoteChannel that uses JGroups + * for communication between group members. + * @param channel JGroups channel. + */ + public VotingAdapter(Channel channel) { + rpcDispatcher = new RpcDispatcher(channel, this, this, this); + } + + public VotingAdapter(PullPushAdapter adapter, Serializable id) { + rpcDispatcher = new RpcDispatcher(adapter, id, this, this, this); + } + + + public Collection getMembers() { + return rpcDispatcher != null? rpcDispatcher.getMembers() : null; + } + + public void addMembershipListener(MembershipListener l) { + if(l != null && !membership_listeners.contains(l)) + membership_listeners.add(l); + } + + public void removeMembershipListener(MembershipListener l) { + if(l != null) + membership_listeners.remove(l); + } + + + /** + * Performs actual voting on the VoteChannel using the JGroups + * facilities for communication. + */ + public boolean vote(Object decree, int consensusType, long timeout) + throws ChannelException { + return vote(decree, consensusType, timeout, null); + } + + /** + * Performs actual voting on the VoteChannel using the JGroups + * facilities for communication. + */ + public boolean vote(Object decree, int consensusType, long timeout, VoteResponseProcessor voteResponseProcessor) + throws ChannelException + { + if (closed) + throw new ChannelException("Channel was closed."); + + + if(log.isDebugEnabled()) log.debug("Conducting voting on decree " + decree + ", consensus type " + + getConsensusStr(consensusType) + ", timeout " + timeout); + + int mode = GroupRequest.GET_ALL; + + // perform the consensus mapping + switch (consensusType) { + case VotingAdapter.VOTE_ALL : mode = GroupRequest.GET_ALL; break; + case VotingAdapter.VOTE_ANY : mode = GroupRequest.GET_FIRST; break; + case VotingAdapter.VOTE_MAJORITY : mode = GroupRequest.GET_MAJORITY; break; + default : mode = GroupRequest.GET_ALL; + } + + try { + java.lang.reflect.Method method = this.getClass().getMethod( + "localVote", new Class[] { Object.class }); + + MethodCall methodCall = new MethodCall(method, new Object[] {decree}); + + + if(log.isDebugEnabled()) log.debug("Calling remote methods..."); + + // vote + RspList responses = rpcDispatcher.callRemoteMethods( + null, methodCall, mode, timeout); + + + if(log.isDebugEnabled()) log.debug("Checking responses."); + + if (voteResponseProcessor == null) { + voteResponseProcessor = this; + } + + return voteResponseProcessor.processResponses(responses, consensusType, decree); + } catch(NoSuchMethodException nsmex) { + + // UPS!!! How can this happen?! + + if(log.isErrorEnabled()) log.error("Could not find method localVote(Object). " + + nsmex.toString()); + + throw new UnsupportedOperationException( + "Cannot execute voting because of absence of " + + this.getClass().getName() + ".localVote(Object) method."); + } + } + + + /** + * Processes the response list and makes a decision according to the + * type of the consensus for current voting. + *

+ * Note: we do not support voting in case of Byzantine failures, i.e. + * when the node responds with the fault message. + */ + public boolean processResponses(RspList responses, int consensusType, Object decree) + throws ChannelException + { + if (responses == null) { + return false; + } + + boolean voteResult = false; + int totalPositiveVotes = 0; + int totalNegativeVotes = 0; + + for(Iterator it=responses.values().iterator(); it.hasNext();) { + Rsp response = (Rsp)it.next(); + + switch(checkResponse(response)) { + case PROCESS_SKIP : continue; + case PROCESS_BREAK : return false; + } + + VoteResult result = (VoteResult)response.getValue(); + + totalPositiveVotes += result.getPositiveVotes(); + totalNegativeVotes += result.getNegativeVotes(); + } + + switch(consensusType) { + case VotingAdapter.VOTE_ALL : + voteResult = (totalNegativeVotes == 0 && totalPositiveVotes > 0); + break; + case VotingAdapter.VOTE_ANY : + voteResult = (totalPositiveVotes > 0); + break; + case VotingAdapter.VOTE_MAJORITY : + voteResult = (totalPositiveVotes > totalNegativeVotes); + } + + return voteResult; + } + + /** + * This method checks the response and says the processResponses() method + * what to do. + * @return PROCESS_CONTINUE to continue calculating votes, + * PROCESS_BREAK to stop calculating votes from the nodes, + * PROCESS_SKIP to skip current response. + * @throws ChannelException when the response is fatal to the + * current voting process. + */ + private int checkResponse(Rsp response) throws ChannelException { + + if (!response.wasReceived()) { + + + if(log.isDebugEnabled()) log.debug("Response from node " + response.getSender() + + " was not received."); + + // what do we do when one node failed to respond? + //throw new ChannelException("Node " + response.GetSender() + + // " failed to respond."); + return PROCESS_BREAK ; + } + + /**@todo check what to do here */ + if (response.wasSuspected()) { + if(log.isDebugEnabled()) log.debug("Node " + response.getSender() + " was suspected."); + + // wat do we do when one node is suspected? + return PROCESS_SKIP ; + } + + Object object = response.getValue(); + + // we received exception/error, something went wrong + // on one of the nodes... and we do not handle such faults + if (object instanceof Throwable) { + throw new ChannelException("Node " + response.getSender() + + " is faulty."); + } + + if (object == null) { + return PROCESS_SKIP; + } + + // it is always interesting to know the class that caused failure... + if (!(object instanceof VoteResult)) { + String faultClass = object.getClass().getName(); + + // ...but we do not handle byzantine faults + throw new ChannelException("Node " + response.getSender() + + " generated fault (class " + faultClass + ')'); + } + + // what if we received the response from faulty node? + if (object instanceof FailureVoteResult) { + + if(log.isErrorEnabled()) log.error(((FailureVoteResult)object).getReason()); + + return PROCESS_BREAK; + } + + // everything is fine :) + return PROCESS_CONTINUE; + } + + /** + * Callback for notification about the new view of the group. + */ + public void viewAccepted(View newView) { + + // clean nodes that were suspected but still exist in new view + Iterator iterator = suspectedNodes.iterator(); + while(iterator.hasNext()) { + Address suspectedNode = (Address)iterator.next(); + if (newView.containsMember(suspectedNode)) + iterator.remove(); + } + + for(Iterator it=membership_listeners.iterator(); it.hasNext();) { + MembershipListener listener=(MembershipListener)it.next(); + try { + listener.viewAccepted(newView); + } + catch(Throwable t) { + if(log.isErrorEnabled()) + log.error("failed calling viewAccepted() on " + listener, t); + } + } + } + + /** + * Callback for notification that one node is suspected + */ + public void suspect(Address suspected) { + suspectedNodes.add(suspected); + for(Iterator it=membership_listeners.iterator(); it.hasNext();) { + MembershipListener listener=(MembershipListener)it.next(); + try { + listener.suspect(suspected); + } + catch(Throwable t) { + if(log.isErrorEnabled()) + log.error("failed calling suspect() on " + listener, t); + } + } + } + + /** + * Blocks the channel until the ViewAccepted is invoked. + */ + public void block() { + for(Iterator it=membership_listeners.iterator(); it.hasNext();) { + MembershipListener listener=(MembershipListener)it.next(); + try { + listener.block(); + } + catch(Throwable t) { + if(log.isErrorEnabled()) + log.error("failed calling block() on " + listener, t); + } + } + } + + /** + * Get the channel state. + * + * @return always null, we do not have any group-shared + * state. + */ + public byte[] getState() { + return null; + } + + /** + * Receive the message. All messages are ignored. + * + * @param msg message to check. + */ + public void receive(org.jgroups.Message msg) { + // do nothing + } + + /** + * Set the channel state. We do nothing here. + */ + public void setState(byte[] state) { + // ignore the state, we do not have any. + } + + private final Set voteListeners = new HashSet(); + private VotingListener[] listeners; + + /** + * Vote on the specified decree requiring all nodes to vote. + * + * @param decree decree on which nodes should vote. + * @param timeout time during which nodes can vote. + * + * @return true if nodes agreed on a decree, otherwise + * false + * + * @throws ChannelException if something went wrong. + */ + public boolean vote(Object decree, long timeout) throws ChannelException { + return vote(decree, timeout, null); + } + + /** + * Vote on the specified decree requiring all nodes to vote. + * + * @param decree decree on which nodes should vote. + * @param timeout time during which nodes can vote. + * @param voteResponseProcessor processor which will be called for every response that is received. + * + * @return true if nodes agreed on a decree, otherwise + * false + * + * @throws ChannelException if something went wrong. + */ + public boolean vote(Object decree, long timeout, VoteResponseProcessor voteResponseProcessor) throws ChannelException { + return vote(decree, VOTE_ALL, timeout, voteResponseProcessor); + } + + /** + * Adds voting listener. + */ + public void addVoteListener(VotingListener listener) { + voteListeners.add(listener); + listeners = (VotingListener[])voteListeners.toArray( + new VotingListener[voteListeners.size()]); + } + + /** + * Removes voting listener. + */ + public void removeVoteListener(VotingListener listener) { + voteListeners.remove(listener); + + listeners = (VotingListener[])voteListeners.toArray( + new VotingListener[voteListeners.size()]); + } + + /** + * This method performs voting on the specific decree between all + * local voteListeners. + */ + public VoteResult localVote(Object decree) { + + VoteResult voteResult = new VoteResult(); + + for(int i = 0; i < listeners.length; i++) { + VotingListener listener = listeners[i]; + + try { + voteResult.addVote(listener.vote(decree)); + } catch (VoteException vex) { + // do nothing here. + } catch(RuntimeException ex) { + + if(log.isErrorEnabled()) log.error(ex.toString()); + + // if we are here, then listener + // had thrown a RuntimeException + return new FailureVoteResult(ex.getMessage()); + } + } + + + if(log.isDebugEnabled()) log.debug("Voting on decree " + decree.toString() + " : " + + voteResult.toString()); + + return voteResult; + } + + /** + * Convert consensus type into string representation. This method is + * useful for debugginf. + * + * @param consensusType type of the consensus. + * + * @return string representation of the consensus type. + */ + public static String getConsensusStr(int consensusType) { + switch(consensusType) { + case VotingAdapter.VOTE_ALL : return "VOTE_ALL"; + case VotingAdapter.VOTE_ANY : return "VOTE_ANY"; + case VotingAdapter.VOTE_MAJORITY : return "VOTE_MAJORITY"; + default : return "UNKNOWN"; + } + } + + /** + * This class represents the result of local voting. It contains a + * number of positive and negative votes collected during local voting. + */ + public static class VoteResult implements Serializable { + private int positiveVotes = 0; + private int negativeVotes = 0; + private static final long serialVersionUID = 2868605599965196746L; + + public void addVote(boolean vote) { + if (vote) + positiveVotes++; + else + negativeVotes++; + } + + public int getPositiveVotes() { return positiveVotes; } + + public int getNegativeVotes() { return negativeVotes; } + + public String toString() { + return "VoteResult: up=" + positiveVotes + + ", down=" + negativeVotes; + } + } + + /** + * Class that represents a result of local voting on the failed node. + */ + public static class FailureVoteResult extends VoteResult { + private final String reason; + + public FailureVoteResult(String reason) { + this.reason = reason; + } + + public String getReason() { + return reason; + } + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/blocks/VotingListener.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/blocks/VotingListener.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/blocks/VotingListener.java 17 Aug 2012 14:51:18 -0000 1.1 @@ -0,0 +1,22 @@ +package org.jgroups.blocks; + +/** + * Implemetations of this interface are able to participate in voting process. + * + * @author Roman Rokytskyy (rrokytskyy@acm.org) + */ +public interface VotingListener { + /** + * Each member is able to vote with true or false + * messages. If the member does not know what to do with the + * decree it should throw VoteException. Doing + * this he will be excluded from voting process and will not influence + * the result. + * + * @param decree object representing the decree of current voting. + * + * @throws VoteException if listener does not know the meaning of the + * decree and wants to be excluded from this voting. + */ + boolean vote(Object decree) throws VoteException; +} \ No newline at end of file Index: 3rdParty_sources/jgroups/org/jgroups/blocks/package.html =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/blocks/package.html,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/blocks/package.html 17 Aug 2012 14:51:18 -0000 1.1 @@ -0,0 +1,13 @@ + + + Provides building blocks that are layered on top of channels. + Most of them do not even need a channel, all they need is a class + that implements interface Transport (channels do). + This enables them to work on any type of group transport that obeys this interface. + Building blocks can be used instead of channels whenever a higher-level interface + is required. Whereas channels are simple socket-like constructs, building blocks + may offer a far more sophisticated interface. In some cases, building blocks offer + access to the underlying channel, so that - if the building block at hand does not + offer a certain functionality - the channel can be accessed directly. + + \ No newline at end of file Index: 3rdParty_sources/jgroups/org/jgroups/conf/ClassConfigurator.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/conf/ClassConfigurator.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/conf/ClassConfigurator.java 17 Aug 2012 14:51:26 -0000 1.1 @@ -0,0 +1,235 @@ +// $Id: ClassConfigurator.java,v 1.1 2012/08/17 14:51:26 marcin Exp $ + +package org.jgroups.conf; + + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.ChannelException; +import org.jgroups.Global; +import org.jgroups.util.Util; + +import java.io.ObjectStreamClass; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +/** + * This class will be replaced with the class that read info + * from the magic number configurator that reads info from the xml file. + * The name and the relative path of the magic number map file can be specified + * as value of the property org.jgroups.conf.magicNumberFile. + * It must be relative to one of the classpath elements, to allow the + * classloader to locate the file. If a value is not specified, + * MagicNumberReader.MAGIC_NUMBER_FILE is used, which defaults + * to "jg-magic-map.xml". + * + * @author Filip Hanik + * @author Bela Ban + * @see MagicNumberReader + */ +public class ClassConfigurator { + static volatile ClassConfigurator instance=null; // works under the new JSR 133 memory model in JDK 5 + private static final short MIN_CUSTOM_MAGIC_NUMBER=1024; + + //this is where we store magic numbers + private final Map classMap=new ConcurrentHashMap(); // key=Class, value=magic number + private final Map magicMap=new ConcurrentHashMap(); // key=magic number, value=Class + + /** Map */ + private final Map streamMapId=new HashMap(); + + /** Map */ + private final Map streamMapClass=new HashMap(); + + protected final Log log=LogFactory.getLog(getClass()); + + + public ClassConfigurator() { + } + + public void init() throws ChannelException { + //populate the map + try { + // make sure we have a class for DocumentBuilderFactory + // getClass().getClassLoader().loadClass("javax.xml.parsers.DocumentBuilderFactory"); + Util.loadClass("javax.xml.parsers.DocumentBuilderFactory", this.getClass()); + + MagicNumberReader reader=new MagicNumberReader(); + + // PropertyPermission not granted if running in an untrusted environment with JNLP. + try { + String mnfile=Util.getProperty(new String[]{Global.MAGIC_NUMBER_FILE, "org.jgroups.conf.magicNumberFile"}, + null, null, false, null); + if(mnfile != null) { + if(log.isDebugEnabled()) log.debug("Using " + mnfile + " as magic number file"); + reader.setFilename(mnfile); + } + } + catch (SecurityException ex){ + } + + ObjectStreamClass objStreamClass; + ClassMap[] mapping=reader.readMagicNumberMapping(); + if(mapping != null) { + Short m; + for(int i=0; i < mapping.length; i++) { + m=new Short(mapping[i].getMagicNumber()); + try { + Class clazz=mapping[i].getClassForMap(); + objStreamClass=ObjectStreamClass.lookup(clazz); + if(objStreamClass == null) + throw new ChannelException("ObjectStreamClass for " + clazz + " not found"); + if(magicMap.containsKey(m)) { + throw new ChannelException("magic key " + m + " (" + clazz.getName() + ')' + + " is already in map; please make sure that " + + "all magic keys are unique"); + } + else { + magicMap.put(m, clazz); + classMap.put(clazz, m); + + streamMapId.put(m, objStreamClass); + streamMapClass.put(objStreamClass, m); + } + } + catch(ClassNotFoundException cnf) { + throw new ChannelException("failed loading class", cnf); + } + } + if(log.isDebugEnabled()) log.debug("mapping is:\n" + printMagicMap()); + } + } + catch(ChannelException ex) { + throw ex; + } + catch(Throwable x) { + // if(log.isErrorEnabled()) log.error("failed reading the magic number mapping file, reason: " + Util.print(x)); + throw new ChannelException("failed reading the magic number mapping file", x); + } + } + + + public static ClassConfigurator getInstance(boolean init) throws ChannelException { + if(instance == null) { + instance=new ClassConfigurator(); + if(init) + instance.init(); + } + return instance; + } + + public static ClassConfigurator getInstance() throws ChannelException { + return getInstance(false); + } + + /** + * Method to register a user-defined header with jg-magic-map at runtime + * @param magic The magic number. Needs to be > 1024 + * @param clazz The class. Usually a subclass of Header + * @throws IllegalArgumentException If the magic number is already taken, or the magic number is <= 1024 + */ + public void add(short magic, Class clazz) throws IllegalArgumentException { + if(magic <= MIN_CUSTOM_MAGIC_NUMBER) + throw new IllegalArgumentException("magic number (" + magic + ") needs to be greater than " + + MIN_CUSTOM_MAGIC_NUMBER); + if(magicMap.containsKey(magic) || classMap.containsKey(clazz)) + throw new IllegalArgumentException("magic number " + magic + " for class " + clazz.getName() + + " is already present"); + magicMap.put(magic, clazz); + classMap.put(clazz, magic); + } + + /** + * Returns a class for a magic number. + * Returns null if no class is found + * + * @param magic the magic number that maps to the class + * @return a Class object that represents a class that implements java.io.Externalizable + */ + public Class get(short magic) { + return magicMap.get(magic); + } + + /** + * Loads and returns the class from the class name + * + * @param clazzname a fully classified class name to be loaded + * @return a Class object that represents a class that implements java.io.Externalizable + */ + public Class get(String clazzname) { + try { + // return ClassConfigurator.class.getClassLoader().loadClass(clazzname); + return Util.loadClass(clazzname, this.getClass()); + } + catch(Exception x) { + if(log.isErrorEnabled()) log.error("failed loading class " + clazzname, x); + } + return null; + } + + /** + * Returns the magic number for the class. + * + * @param clazz a class object that we want the magic number for + * @return the magic number for a class, -1 if no mapping is available + */ + public short getMagicNumber(Class clazz) { + Short i=classMap.get(clazz); + if(i == null) + return -1; + else + return i; + } + + public short getMagicNumberFromObjectStreamClass(ObjectStreamClass objStream) { + Short i=streamMapClass.get(objStream); + if(i == null) + return -1; + else + return i.shortValue(); + } + + public ObjectStreamClass getObjectStreamClassFromMagicNumber(short magic_number) { + ObjectStreamClass retval=null; + retval=streamMapId.get(magic_number); + return retval; + } + + + public String toString() { + return printMagicMap(); + } + + public String printMagicMap() { + StringBuilder sb=new StringBuilder(); + SortedSet keys=new TreeSet(magicMap.keySet()); + + for(Short key: keys) { + sb.append(key).append(":\t").append(magicMap.get(key)).append('\n'); + } + return sb.toString(); + } + + public String printClassMap() { + StringBuilder sb=new StringBuilder(); + Map.Entry entry; + + for(Iterator it=classMap.entrySet().iterator(); it.hasNext();) { + entry=(Map.Entry)it.next(); + sb.append(entry.getKey()).append(": ").append(entry.getValue()).append('\n'); + } + return sb.toString(); + } + + + + /* --------------------------------- Private methods ------------------------------------ */ + + /* ------------------------------ End of Pivate methods --------------------------------- */ + public static void main(String[] args) + throws Exception { + + ClassConfigurator test=getInstance(true); + System.out.println('\n' + test.printMagicMap()); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/conf/ClassMap.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/conf/ClassMap.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/conf/ClassMap.java 17 Aug 2012 14:51:26 -0000 1.1 @@ -0,0 +1,53 @@ +package org.jgroups.conf; + +import org.jgroups.util.Util; + + +/** + * Maintains mapping between magic number and class + * + * @author Filip Hanik (filip@filip.net) + * @author Bela Ban + * @version 1.0 + */ +public class ClassMap { + private final String mClassname; + private final short mMagicNumber; + + public ClassMap(String clazz, short magicnumber) { + mClassname=clazz; + mMagicNumber=magicnumber; + } + + public int hashCode() { + return getMagicNumber(); + } + + public String getClassName() { + return mClassname; + } + + public short getMagicNumber() { + return mMagicNumber; + } + + + /** + * Returns the Class object for this class
+ */ + public Class getClassForMap() throws ClassNotFoundException { + return Util.loadClass(getClassName(), this.getClass()); + } + + + public boolean equals(Object another) { + if(another instanceof ClassMap) { + ClassMap obj=(ClassMap)another; + return getClassName().equals(obj.getClassName()); + } + else + return false; + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/conf/ConfiguratorFactory.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/conf/ConfiguratorFactory.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/conf/ConfiguratorFactory.java 17 Aug 2012 14:51:26 -0000 1.1 @@ -0,0 +1,548 @@ + +package org.jgroups.conf; + +import org.w3c.dom.Element; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.jgroups.ChannelException; +import org.jgroups.JChannel; +import org.jgroups.util.Util; + +import java.io.*; + +import java.net.MalformedURLException; +import java.net.URL; + +import java.util.Properties; +import java.util.HashMap; +import java.util.Map; +import java.util.Iterator; +import java.security.AccessControlException; + +/** + * The ConfigurationFactory is a factory that returns a protocol stack configurator. + * The protocol stack configurator is an object that read a stack configuration and + * parses it so that the ProtocolStack can create a stack. + *
+ * Currently the factory returns one of the following objects:
+ * 1. XmlConfigurator - parses XML files
+ * 2. PlainConfigurator - uses the old style strings UDP:FRAG: etc etc
+ * + * @author Filip Hanik (
filip@filip.net) + * @author Bela Ban + * @version $Id: ConfiguratorFactory.java,v 1.1 2012/08/17 14:51:26 marcin Exp $ + */ +public class ConfiguratorFactory { + public static final String JAXP_MISSING_ERROR_MSG= + "JAXP Error: the required XML parsing classes are not available; " + + "make sure that JAXP compatible libraries are in the classpath."; + + static final String FORCE_CONFIGURATION="force.properties"; + + static final Log log=LogFactory.getLog(ConfiguratorFactory.class); + + static String propertiesOverride=null; + + // Check for the presence of the system property "force.properties", and + // act appropriately if it is set. We only need to do this once since the + // system properties are highly unlikely to change. + static { + try { + Properties properties = System.getProperties(); + propertiesOverride = properties.getProperty(FORCE_CONFIGURATION); + } + catch (SecurityException e) { + propertiesOverride = null; + } + + if(propertiesOverride != null && log.isDebugEnabled()) { + log.debug("using properties override: " + propertiesOverride); + } + } + + protected ConfiguratorFactory() { + } + + /** + * Returns a protocol stack configurator based on the XML configuration + * provided by the specified File. + * + * @param file a File with a JGroups XML configuration. + * + * @return a ProtocolStackConfigurator containing the stack + * configuration. + * + * @throws ChannelException if problems occur during the configuration of + * the protocol stack. + */ + public static ProtocolStackConfigurator getStackConfigurator(File file) throws ChannelException { + ProtocolStackConfigurator returnValue; + + if (propertiesOverride != null) { + returnValue = getStackConfigurator(propertiesOverride); + } + else { + try { + checkJAXPAvailability(); + InputStream input=getConfigStream(file); + returnValue=XmlConfigurator.getInstance(input); + } + catch(Exception ex) { + throw createChannelConfigurationException(ex); + } + } + return returnValue; + } + + /** + * Returns a protocol stack configurator based on the XML configuration + * provided at the specified URL. + * + * @param url a URL pointing to a JGroups XML configuration. + * + * @return a ProtocolStackConfigurator containing the stack + * configuration. + * + * @throws ChannelException if problems occur during the configuration of + * the protocol stack. + */ + public static ProtocolStackConfigurator getStackConfigurator(URL url) throws ChannelException { + ProtocolStackConfigurator returnValue; + + if (propertiesOverride != null) { + returnValue = getStackConfigurator(propertiesOverride); + } + else { + checkForNullConfiguration(url); + checkJAXPAvailability(); + + try { + returnValue=XmlConfigurator.getInstance(url); + } + catch (IOException ioe) { + throw createChannelConfigurationException(ioe); + } + } + + return returnValue; + } + + /** + * Returns a protocol stack configurator based on the XML configuration + * provided by the specified XML element. + * + * @param element a XML element containing a JGroups XML configuration. + * + * @return a ProtocolStackConfigurator containing the stack + * configuration. + * + * @throws ChannelException if problems occur during the configuration of + * the protocol stack. + */ + public static ProtocolStackConfigurator getStackConfigurator(Element element) throws ChannelException { + ProtocolStackConfigurator returnValue; + + if (propertiesOverride != null) { + returnValue = getStackConfigurator(propertiesOverride); + } + else { + checkForNullConfiguration(element); + + // Since Element is a part of the JAXP specification and because an + // Element instance already exists, there is no need to check for + // JAXP availability. + // + // checkJAXPAvailability(); + + try { + returnValue=XmlConfigurator.getInstance(element); + } + catch (IOException ioe) { + throw createChannelConfigurationException(ioe); + } + } + + return returnValue; + } + + /** + * Returns a protocol stack configurator based on the provided properties + * string. + * + * @param properties an old style property string, a string representing a + * system resource containing a JGroups XML configuration, + * a string representing a URL pointing to a JGroups XML + * XML configuration, or a string representing a file name + * that contains a JGroups XML configuration. + */ + public static ProtocolStackConfigurator getStackConfigurator(String properties) throws ChannelException { + if (propertiesOverride != null) { + properties = propertiesOverride; + } + + // added by bela: for null String props we use the default properties + if(properties == null) + properties=JChannel.DEFAULT_PROTOCOL_STACK; + + checkForNullConfiguration(properties); + + ProtocolStackConfigurator returnValue; + + // Attempt to treat the properties string as a pointer to an XML + // configuration. + XmlConfigurator configurator = null; + + try { + configurator=getXmlConfigurator(properties); + } + catch (IOException ioe) { + throw createChannelConfigurationException(ioe); + } + + // Did the properties string point to a JGroups XML configuration? + if (configurator != null) { + returnValue=configurator; + } + else { + // Attempt to process the properties string as the old style + // property string. + returnValue=new PlainConfigurator(properties); + } + + return returnValue; + } + + /** + * Returns a protocol stack configurator based on the properties passed in.
+ * If the properties parameter is a plain string UDP:FRAG:MERGE:GMS etc, a PlainConfigurator is returned.
+ * If the properties parameter is a string that represents a url for example http://www.filip.net/test.xml + * or the parameter is a java.net.URL object, an XmlConfigurator is returned
+ * + * @param properties old style property string, url string, or java.net.URL object + * @return a ProtocolStackConfigurator containing the stack configuration + * @throws IOException if it fails to parse the XML content + * @throws IOException if the URL is invalid or a the content can not be reached + * @deprecated Used by the JChannel(Object) constructor which has been deprecated. + */ + public static ProtocolStackConfigurator getStackConfigurator(Object properties) throws IOException { + InputStream input=null; + + if (propertiesOverride != null) { + properties = propertiesOverride; + } + + // added by bela: for null String props we use the default properties + if(properties == null) + properties=JChannel.DEFAULT_PROTOCOL_STACK; + + if(properties instanceof URL) { + try { + input=((URL)properties).openStream(); + } + catch(Throwable t) { + } + } + + // if it is a string, then it could be a plain string or a url + if(input == null && properties instanceof String) { + try { + input=new URL((String)properties).openStream(); + } + catch(Exception ignore) { + // if we get here this means we don't have a URL + } + + // another try - maybe it is a resource, e.g. udp.xml + if(input == null && ((String)properties).endsWith("xml")) { + try { + input=Util.getResourceAsStream((String)properties, ConfiguratorFactory.class); + } + catch(Throwable ignore) { + } + } + + // try a regular file name + // + // This code was moved from the parent block (below) because of the + // possibility of causing a ClassCastException. + + if(input == null) { + try { + input=new FileInputStream((String)properties); + } + catch(Throwable t) { + } + } + } + + // try a regular file + if(input == null && properties instanceof File) { + try { + input=new FileInputStream((File)properties); + } + catch(Throwable t) { + } + } + + if(input != null) { + return XmlConfigurator.getInstance(input); + } + + if(properties instanceof Element) { + return XmlConfigurator.getInstance((Element)properties); + } + + return new PlainConfigurator((String)properties); + } + + + + public static InputStream getConfigStream(File file) throws Exception { + if(propertiesOverride != null) + return getConfigStream(propertiesOverride); + + checkForNullConfiguration(file); + + try { + return new FileInputStream(file); + } + catch(IOException ioe) { + throw createChannelConfigurationException(ioe); + } + } + + + + public static InputStream getConfigStream(URL url) throws Exception { + if (propertiesOverride != null) + return getConfigStream(propertiesOverride); + try { + checkJAXPAvailability(); + return url.openStream(); + } + catch(Exception ex) { + throw createChannelConfigurationException(ex); + } + } + + + + /** + * Returns a JGroups XML configuration InputStream based on the provided + * properties string. + * + * @param properties a string representing a system resource containing a + * JGroups XML configuration, a string representing a URL + * pointing to a JGroups ML configuration, or a string + * representing a file name that contains a JGroups XML + * configuration. + * + * @throws IOException if the provided properties string appears to be a + * valid URL but is unreachable. + */ + public static InputStream getConfigStream(String properties) throws IOException { + InputStream configStream = null; + if (propertiesOverride != null) + return getConfigStream(propertiesOverride); + + // Check to see if the properties string is the name of a file. + try { + configStream=new FileInputStream(properties); + } + catch(FileNotFoundException fnfe) { + // the properties string is likely not a file + } + catch(AccessControlException access_ex) { + // fixes http://jira.jboss.com/jira/browse/JGRP-94 + } + + // Check to see if the properties string is a URL. + if(configStream == null) { + try { + configStream=new URL(properties).openStream(); + } + catch (MalformedURLException mre) { + // the properties string is not a URL + } + } + // Commented so the caller is notified of this condition, but left in + // the code for documentation purposes. + // + // catch (IOException ioe) { + // the specified URL string was not reachable + // } + + // Check to see if the properties string is the name of a resource, + // e.g. udp.xml. + if(configStream == null && properties.endsWith("xml")) { + configStream=Util.getResourceAsStream(properties, ConfiguratorFactory.class); + } + return configStream; + } + + + public static InputStream getConfigStream(Object properties) throws IOException { + InputStream input=null; + if (propertiesOverride != null) + return getConfigStream(propertiesOverride); + + // added by bela: for null String props we use the default properties + if(properties == null) + properties=JChannel.DEFAULT_PROTOCOL_STACK; + + if(properties instanceof URL) { + try { + input=((URL)properties).openStream(); + } + catch(Throwable t) { + } + } + + // if it is a string, then it could be a plain string or a url + if(input == null && properties instanceof String) { + input=getConfigStream((String)properties); + } + + // try a regular file + if(input == null && properties instanceof File) { + try { + input=new FileInputStream((File)properties); + } + catch(Throwable t) { + } + } + + if(input != null) + return input; + + if(properties instanceof Element) { + return getConfigStream((Element)properties); + } + + return new ByteArrayInputStream(((String)properties).getBytes()); + } + + + + + /** + * Returns an XmlConfigurator based on the provided properties string (if + * possible). + * + * @param properties a string representing a system resource containing a + * JGroups XML configuration, a string representing a URL + * pointing to a JGroups ML configuration, or a string + * representing a file name that contains a JGroups XML + * configuration. + * + * @return an XmlConfigurator instance based on the provided properties + * string; null if the provided properties string does + * not point to an XML configuration. + * + * @throws IOException if the provided properties string appears to be a + * valid URL but is unreachable, or if the JGroups XML + * configuration pointed to by the URL can not be + * parsed. + */ + static XmlConfigurator getXmlConfigurator(String properties) throws IOException { + XmlConfigurator returnValue=null; + InputStream configStream=getConfigStream(properties); + + if (configStream != null) { + checkJAXPAvailability(); + returnValue=XmlConfigurator.getInstance(configStream); + } + + return returnValue; + } + + /** + * Creates a ChannelException instance based upon a + * configuration problem. + * + * @param cause the exceptional configuration condition to be used as the + * created ChannelException's cause. + */ + static ChannelException createChannelConfigurationException(Throwable cause) { + return new ChannelException("unable to load the protocol stack", cause); + } + + /** + * Check to see if the specified configuration properties are + * null which is not allowed. + * + * @param properties the specified protocol stack configuration. + * + * @throws NullPointerException if the specified configuration properties + * are null. + */ + static void checkForNullConfiguration(Object properties) { + if(properties == null) + throw new NullPointerException("the specifed protocol stack configuration was null"); + } + + /** + * Checks the availability of the JAXP classes on the classpath. + * + * @throws NoClassDefFoundError if the required JAXP classes are not + * availabile on the classpath. + */ + static void checkJAXPAvailability() { + try { + // TODO: Do some real class checking here instead of forcing the + // load of a JGroups class that happens (by default) to do it + // for us. + XmlConfigurator.class.getName(); + } + catch (NoClassDefFoundError error) { + Error tmp=new NoClassDefFoundError(JAXP_MISSING_ERROR_MSG); + tmp.initCause(error); + throw tmp; + } + } + + /** Replace variables of the form ${var:default} with the getProperty(var, default) + * @param configurator + */ + public static void substituteVariables(ProtocolStackConfigurator configurator) { + ProtocolData[] protocols; + + try { + protocols=configurator.getProtocolStack(); + } + catch(Exception e) { + protocols=null; + } + + if(protocols == null) + return; + for(int i=0; i < protocols.length; i++) { + ProtocolData protocol=protocols[i]; + if(protocol != null) { + HashMap parms=protocol.getParameters(); + Map.Entry entry; + ProtocolParameter parm; + for(Iterator it=parms.entrySet().iterator(); it.hasNext();) { + entry=(Map.Entry)it.next(); + parm=(ProtocolParameter)entry.getValue(); + String val=parm.getValue(); + String replacement=Util.substituteVariable(val); + if(!replacement.equals(val)) { + parm.setValue(replacement); + } + } + } + } + + } +} + + + + + + + + + Index: 3rdParty_sources/jgroups/org/jgroups/conf/MagicNumberReader.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/conf/MagicNumberReader.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/conf/MagicNumberReader.java 17 Aug 2012 14:51:26 -0000 1.1 @@ -0,0 +1,109 @@ +package org.jgroups.conf; + +/** + * Reads and maintains mapping between magic numbers and classes + * @author Filip Hanik (
filip@filip.net) + * @author Bela Ban + * @version 1.0 + */ + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.util.Util; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.NamedNodeMap; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.io.IOException; + +public class MagicNumberReader { + public static final String MAGIC_NUMBER_FILE="jg-magic-map.xml"; + + public String mMagicNumberFile=MAGIC_NUMBER_FILE; + + protected static final Log log=LogFactory.getLog(MagicNumberReader.class); + + public void setFilename(String file) { + mMagicNumberFile=file; + } + + /** + * try to read the magic number configuration file as a Resource form the classpath using getResourceAsStream + * if this fails this method tries to read the configuration file from mMagicNumberFile using a FileInputStream (not in classpath but somewhere else in the disk) + * + * @return an array of ClassMap objects that where parsed from the file (if found) or an empty array if file not found or had en exception + */ + public ClassMap[] readMagicNumberMapping() { + try { + InputStream stream=Util.getResourceAsStream(mMagicNumberFile, this.getClass()); + // try to load the map from file even if it is not a Resource in the class path + if(stream == null) { + try { + if(log.isTraceEnabled()) + log.trace("Could not read " + mMagicNumberFile + " as Resource from the CLASSPATH, will try to read it from file."); + stream=new FileInputStream(mMagicNumberFile); + if(log.isTraceEnabled()) + log.trace("Magic number file found at '" + mMagicNumberFile + '\''); + } + catch(FileNotFoundException fnfe) { + if(log.isWarnEnabled()) + log.warn("Failed reading - '" + mMagicNumberFile + "' is not found, got error '" + + fnfe.getLocalizedMessage() + "'. Please make sure it is in the CLASSPATH or in the " + + "specified location. Will continue, but marshalling will be slower"); + } + } + + if(stream == null) { + return new ClassMap[0]; + } + return parse(stream); + } + catch(Exception x) { + if(log.isErrorEnabled()) log.error("failed reading magic map", x); + } + return new ClassMap[0]; + } + + protected static ClassMap[] parse(InputStream stream) throws Exception { + DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance(); + factory.setValidating(false); //for now + DocumentBuilder builder=factory.newDocumentBuilder(); + Document document=builder.parse(stream); + NodeList class_list=document.getElementsByTagName("class"); + java.util.Vector v=new java.util.Vector(); + for(int i=0; i < class_list.getLength(); i++) { + if(class_list.item(i).getNodeType() == Node.ELEMENT_NODE) { + v.addElement(parseClassData(class_list.item(i))); + } + } + ClassMap[] data=new ClassMap[v.size()]; + v.copyInto(data); + return data; + } + + protected static ClassMap parseClassData(Node protocol) throws java.io.IOException { + try { + protocol.normalize(); + NamedNodeMap attrs=protocol.getAttributes(); + String clazzname=null; + String magicnumber=null; + + magicnumber=attrs.getNamedItem("id").getNodeValue(); + clazzname=attrs.getNamedItem("name").getNodeValue(); + return new ClassMap(clazzname, Short.valueOf(magicnumber)); + } + catch(Exception x) { + IOException tmp=new IOException(); + tmp.initCause(x); + throw tmp; + } + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/conf/PlainConfigurator.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/conf/PlainConfigurator.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/conf/PlainConfigurator.java 17 Aug 2012 14:51:26 -0000 1.1 @@ -0,0 +1,44 @@ +// $Id: PlainConfigurator.java,v 1.1 2012/08/17 14:51:26 marcin Exp $ + +package org.jgroups.conf; + +/** + * A ProtocolStackConfigurator for the old style properties. + *
+ * Old style properties are referred to as the property string used by channel earlier + * they look like this PROTOCOL(param=value;param=value):PROTOCOL:PROTOCOL
+ * All it does is that it holds the string, it currently doesn't parse it at all. + * + * @author Filip Hanik (
filip@filip.net) + * @version 1.0 + */ + +public class PlainConfigurator implements ProtocolStackConfigurator +{ + private final String mProperties; + + /** + * Instantiates a PlainConfigurator with old style properties + */ + public PlainConfigurator(String properties) + { + mProperties = properties; + } + + /** + * returns the old style protocol string + */ + public String getProtocolStackString() + { + return mProperties; + } + + /** + * Throws a UnsupportedOperationException all the time. No parsing implemented. + */ + public ProtocolData[] getProtocolStack() + { + /**todo: Implement this org.jgroups.conf.ProtocolStackConfigurator method*/ + throw new java.lang.UnsupportedOperationException("Method getProtocolStack() not yet implemented."); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/conf/ProtocolData.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/conf/ProtocolData.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/conf/ProtocolData.java 17 Aug 2012 14:51:26 -0000 1.1 @@ -0,0 +1,124 @@ + +package org.jgroups.conf; + +/** + * Data holder for protocol + * @author Filip Hanik (filip@filip.net) + * @version $Id: ProtocolData.java,v 1.1 2012/08/17 14:51:26 marcin Exp $ + */ + +import java.util.HashMap; +import java.util.Iterator; + +public class ProtocolData { + /** Map of property keys and values */ + private final HashMap mParameters=new HashMap(); + private final String mProtocolName; + private final String mDescription; + private final String mClassName; + private boolean mIsOverRide=false; + + public ProtocolData(String protocolName, + String description, + String className, + ProtocolParameter[] params) { + mProtocolName=protocolName; + mDescription=description; + mClassName=className; + if(params != null) { + for(int i=0; i < params.length; i++) { + mParameters.put(params[i].getName(), params[i]); + } + } + } + + public ProtocolData(String overRideName, + ProtocolParameter[] params) { + this(overRideName, null, null, params); + mIsOverRide=true; + + } + + public String getClassName() { + return mClassName; + } + + public String getProtocolName() { + return mProtocolName; + } + + public String getDescription() { + return mDescription; + } + + public HashMap getParameters() { + return mParameters; + } + + public boolean isOverride() { + return mIsOverRide; + } + + public ProtocolParameter[] getParametersAsArray() { + ProtocolParameter[] result=new ProtocolParameter[mParameters.size()]; + Iterator it=mParameters.keySet().iterator(); + for(int i=0; i < result.length; i++) { + String key=(String)it.next(); + result[i]=(ProtocolParameter)mParameters.get(key); + } + return result; + } + + + public void override(ProtocolParameter[] params) { + for(int i=0; i < params.length; i++) + mParameters.put(params[i].getName(), params[i]); + } + + public String getProtocolString(boolean new_format) { + return new_format? getProtocolStringNewXml() : getProtocolString(); + } + + public String getProtocolString() { + StringBuilder buf=new StringBuilder(mClassName); + if(mParameters.size() > 0) { + buf.append('('); + Iterator i=mParameters.keySet().iterator(); + while(i.hasNext()) { + String key=(String)i.next(); + ProtocolParameter param=(ProtocolParameter)mParameters.get(key); + buf.append(param.getParameterString()); + if(i.hasNext()) buf.append(';'); + }//while + buf.append(')'); + } + return buf.toString(); + } + + public String getProtocolStringNewXml() { + StringBuilder buf=new StringBuilder(mClassName + ' '); + if(mParameters.size() > 0) { + Iterator i=mParameters.keySet().iterator(); + while(i.hasNext()) { + String key=(String)i.next(); + ProtocolParameter param=(ProtocolParameter)mParameters.get(key); + buf.append(param.getParameterStringXml()); + if(i.hasNext()) buf.append(' '); + } + } + return buf.toString(); + } + + public int hashCode() { + return mProtocolName.hashCode(); + } + + public boolean equals(Object another) { + if(another instanceof ProtocolData) + return getProtocolName().equals(((ProtocolData)another).getProtocolName()); + else + return false; + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/conf/ProtocolParameter.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/conf/ProtocolParameter.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/conf/ProtocolParameter.java 17 Aug 2012 14:51:26 -0000 1.1 @@ -0,0 +1,64 @@ + +package org.jgroups.conf; + +/** + * Data holder for protocol data + * + * @author Filip Hanik (filip@filip.net) + * @author Bela Ban + * @version $Id: ProtocolParameter.java,v 1.1 2012/08/17 14:51:26 marcin Exp $ + */ + +public class ProtocolParameter { + + private final String mParameterName; + private String mParameterValue; + + public ProtocolParameter(String parameterName, + String parameterValue) { + mParameterName=parameterName; + mParameterValue=parameterValue; + } + + public String getName() { + return mParameterName; + } + + public String getValue() { + return mParameterValue; + } + + public void setValue(String replacement) { + mParameterValue=replacement; + } + + public int hashCode() { + if(mParameterName != null) + return mParameterName.hashCode(); + else + return -1; + } + + public boolean equals(Object another) { + if(another instanceof ProtocolParameter) + return getName().equals(((ProtocolParameter)another).getName()); + else + return false; + } + + public String getParameterString() { + StringBuilder buf=new StringBuilder(mParameterName); + if(mParameterValue != null) + buf.append('=').append(mParameterValue); + return buf.toString(); + } + + public String getParameterStringXml() { + StringBuilder buf=new StringBuilder(mParameterName); + if(mParameterValue != null) + buf.append("=\"").append(mParameterValue).append('\"'); + return buf.toString(); + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/conf/ProtocolStackConfigurator.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/conf/ProtocolStackConfigurator.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/conf/ProtocolStackConfigurator.java 17 Aug 2012 14:51:26 -0000 1.1 @@ -0,0 +1,14 @@ +// $Id: ProtocolStackConfigurator.java,v 1.1 2012/08/17 14:51:26 marcin Exp $ + +package org.jgroups.conf; + +/** + * @author Filip Hanik (filip@filip.net) + * @version 1.0 + */ + +public interface ProtocolStackConfigurator +{ + String getProtocolStackString(); + ProtocolData[] getProtocolStack(); +} Index: 3rdParty_sources/jgroups/org/jgroups/conf/XmlConfigurator.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/conf/XmlConfigurator.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/conf/XmlConfigurator.java 17 Aug 2012 14:51:26 -0000 1.1 @@ -0,0 +1,517 @@ +// $Id: XmlConfigurator.java,v 1.1 2012/08/17 14:51:26 marcin Exp $ + +package org.jgroups.conf; + +/** + * Uses XML to configure a protocol stack + * @author Filip Hanik (filip@filip.net) + * @version 1.0 + */ + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.w3c.dom.*; +import org.jgroups.stack.Configurator; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.*; + + +public class XmlConfigurator implements ProtocolStackConfigurator { + public static final String ATTR_NAME="name"; + public static final String ATTR_VALUE="value"; + public static final String ATTR_INHERIT="inherit"; + public static final String ELMT_PROT_OVERRIDE="protocol-override"; + public static final String ELMT_PROT="protocol"; + public static final String ELMT_PROT_NAME="protocol-name"; + public static final String ELMT_CLASS="class-name"; + public static final String ELMT_DESCRIPTION="description"; + public static final String ELMT_PROT_PARAMS="protocol-params"; + + private final ArrayList mProtocolStack=new ArrayList(); + private final String mStackName; + protected static final Log log=LogFactory.getLog(XmlConfigurator.class); + + protected XmlConfigurator(String stackName, ProtocolData[] protocols) { + mStackName=stackName; + for(int i=0; i < protocols.length; i++) + mProtocolStack.add(protocols[i]); + } + + protected XmlConfigurator(String stackName) { + this(stackName, new ProtocolData[0]); + } + + + public static XmlConfigurator getInstance(URL url) throws java.io.IOException { + return getInstance(url.openStream()); + } + + public static XmlConfigurator getInstanceOldFormat(URL url) throws java.io.IOException { + return getInstanceOldFormat(url.openStream()); + } + + public static XmlConfigurator getInstance(InputStream stream) throws java.io.IOException { + return parse(stream); + } + + public static XmlConfigurator getInstanceOldFormat(InputStream stream) throws java.io.IOException { + return parseOldFormat(stream); + } + + + public static XmlConfigurator getInstance(Element el) throws java.io.IOException { + return parse(el); + } + + + /** + * + * @param convert If false: print old plain output, else print new XML format + * @return String with protocol stack in specified format + */ + public String getProtocolStackString(boolean convert) { + StringBuilder buf=new StringBuilder(); + Iterator it=mProtocolStack.iterator(); + if(convert) buf.append("\n"); + while(it.hasNext()) { + ProtocolData d=(ProtocolData)it.next(); + if(convert) buf.append(" <"); + buf.append(d.getProtocolString(convert)); + if(convert) buf.append("/>"); + if(it.hasNext()) { + if(convert) + buf.append('\n'); + else + buf.append(':'); + } + } + if(convert) buf.append("\n"); + return buf.toString(); + } + + + public String getProtocolStackString() { + return getProtocolStackString(false); + } + + + public ProtocolData[] getProtocolStack() { + return (ProtocolData[])mProtocolStack.toArray(new ProtocolData[mProtocolStack.size()]); + } + + public String getName() { + return mStackName; + } + + public void override(ProtocolData data) + throws IOException { + int index=mProtocolStack.indexOf(data); + if(index < 0) throw new IOException("You can not override a protocol that doesn't exist"); + ProtocolData source=(ProtocolData)mProtocolStack.get(index); + source.override(data.getParametersAsArray()); + } + + public void add(ProtocolData data) { + mProtocolStack.add(data); + } + + + + protected static XmlConfigurator parseOldFormat(InputStream stream) throws java.io.IOException { + XmlConfigurator configurator=null; + try { + DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance(); + factory.setValidating(false); //for now + DocumentBuilder builder=factory.newDocumentBuilder(); + Document document=builder.parse(stream); + Element root=(Element)document.getElementsByTagName("protocol-stack").item(0); + root.normalize(); + //print("",new PrintWriter(System.out),root); + String stackname=root.getAttribute(ATTR_NAME); + String inherit=root.getAttribute(ATTR_INHERIT); + boolean isinherited=(inherit != null && inherit.length() > 0); + NodeList protocol_list=document.getElementsByTagName(isinherited ? ELMT_PROT_OVERRIDE : ELMT_PROT); + Vector v=new Vector(); + for(int i=0; i < protocol_list.getLength(); i++) { + if(protocol_list.item(i).getNodeType() == Node.ELEMENT_NODE) { + v.addElement(parseProtocolData(protocol_list.item(i))); + } + } + ProtocolData[] protocols=new ProtocolData[v.size()]; + v.copyInto(protocols); + + if(isinherited) { + URL inheritURL=new URL(inherit); + configurator=XmlConfigurator.getInstance(inheritURL); + for(int i=0; i < protocols.length; i++) + configurator.override(protocols[i]); + } + else { + configurator=new XmlConfigurator(stackname, protocols); + } + + } + catch(Exception x) { + if(x instanceof java.io.IOException) + throw (java.io.IOException)x; + else { + IOException tmp=new IOException(); + tmp.initCause(x); + throw tmp; + } + } + return configurator; + } + + + + + protected static XmlConfigurator parse(InputStream stream) throws java.io.IOException { + /** + * CAUTION: crappy code ahead ! I (bela) am not an XML expert, so the code below is pretty amateurish... + * But it seems to work, and it is executed only on startup, so no perf loss on the critical path. + * If somebody wants to improve this, please be my guest. + */ + try { + DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance(); + factory.setValidating(false); //for now + DocumentBuilder builder=factory.newDocumentBuilder(); + Document document=builder.parse(stream); + + // The root element of the document should be the "config" element, + // but the parser(Element) method checks this so a check is not + // needed here. + Element configElement = document.getDocumentElement(); + return parse(configElement); + } + catch(Exception x) { + if(x instanceof java.io.IOException) + throw (java.io.IOException)x; + else { + IOException tmp=new IOException(); + tmp.initCause(x); + throw tmp; + } + } + } + + + + + protected static XmlConfigurator parse(Element root_element) throws java.io.IOException { + XmlConfigurator configurator=null; + + /** LinkedList */ + LinkedList prot_data=new LinkedList(); + + + /** + * CAUTION: crappy code ahead ! I (bela) am not an XML expert, so the code below is pretty amateurish... + * But it seems to work, and it is executed only on startup, so no perf loss on the critical path. + * If somebody wants to improve this, please be my guest. + */ + try { + // NodeList roots=root_element.getChildNodes(); +// for(int i =0; i < roots.getLength(); i++) { +// root=roots.item(i); +// if(root.getNodeType() != Node.ELEMENT_NODE) +// continue; +// } + + String root_name=root_element.getNodeName(); + if(!"config".equals(root_name.trim().toLowerCase())) { + log.fatal("XML protocol stack configuration does not start with a '' element; " + + "maybe the XML configuration needs to be converted to the new format ?\n" + + "use 'java org.jgroups.conf.XmlConfigurator -new_format' to do so"); + throw new IOException("invalid XML configuration"); + } + + NodeList prots=root_element.getChildNodes(); + for(int i=0; i < prots.getLength(); i++) { + Node node = prots.item(i); + if( node.getNodeType() != Node.ELEMENT_NODE ) + continue; + + Element tag = (Element) node; + String protocol = tag.getTagName(); + // System.out.println("protocol: " + protocol); + LinkedList tmp=new LinkedList(); + + NamedNodeMap attrs = tag.getAttributes(); + int attrLength = attrs.getLength(); + for(int a = 0; a < attrLength; a ++) { + Attr attr = (Attr) attrs.item(a); + String name = attr.getName(); + String value = attr.getValue(); + // System.out.println(" name=" + name + ", value=" + value); + tmp.add(new ProtocolParameter(name, value)); + } + ProtocolParameter[] params=new ProtocolParameter[tmp.size()]; + for(int j=0; j < tmp.size(); j++) + params[j]=(ProtocolParameter)tmp.get(j); + ProtocolData data=new ProtocolData(protocol, "bla", protocol, params); + prot_data.add(data); + } + + ProtocolData[] data=new ProtocolData[(prot_data.size())]; + for(int k=0; k < prot_data.size(); k++) + data[k]=(ProtocolData)prot_data.get(k); + configurator=new XmlConfigurator("bla", data); + } + catch(Exception x) { + if(x instanceof java.io.IOException) + throw (java.io.IOException)x; + else { + IOException tmp=new IOException(); + tmp.initCause(x); + throw tmp; + } + } + return configurator; + } + + + protected static ProtocolData parseProtocolData(Node protocol) + throws java.io.IOException { + try { + protocol.normalize(); + boolean isOverride=ELMT_PROT_OVERRIDE.equals(protocol.getNodeName()); + int pos=0; + NodeList children=protocol.getChildNodes(); + /** + * there should be 4 Element Nodes if we are not overriding + * 1. protocol-name + * 2. description + * 3. class-name + * 4. protocol-params + * + * If we are overriding we should have + * 1. protocol-name + * 2. protocol-params + */ + + // + + String name=null; + String clazzname=null; + String desc=null; + ProtocolParameter[] plist=null; + + for(int i=0; i < children.getLength(); i++) { + if(children.item(i).getNodeType() == Node.ELEMENT_NODE) { + pos++; + if(isOverride && (pos == 2)) pos=4; + switch(pos) { + case 1: + name=children.item(i).getFirstChild().getNodeValue(); + break; + case 2: + desc=children.item(i).getFirstChild().getNodeValue(); + break; + case 3: + clazzname=children.item(i).getFirstChild().getNodeValue(); + break; + case 4: + plist=parseProtocolParameters((Element)children.item(i)); + break; + }//switch + }//end if + }//for + + if(isOverride) + return new ProtocolData(name, plist); + else + return new ProtocolData(name, desc, clazzname, plist); + } + catch(Exception x) { + if(x instanceof java.io.IOException) + throw (java.io.IOException)x; + else { + IOException tmp=new IOException(); + tmp.initCause(x); + throw tmp; + } + } + } + + protected static ProtocolParameter[] parseProtocolParameters(Element protparams) throws IOException { + try { + Vector v=new Vector(); + protparams.normalize(); + NodeList parameters=protparams.getChildNodes(); + for(int i=0; i < parameters.getLength(); i++) { + if(parameters.item(i).getNodeType() == Node.ELEMENT_NODE) { + String pname=parameters.item(i).getAttributes().getNamedItem(ATTR_NAME).getNodeValue(); + String pvalue=parameters.item(i).getAttributes().getNamedItem(ATTR_VALUE).getNodeValue(); + ProtocolParameter p=new ProtocolParameter(pname, pvalue); + v.addElement(p); + }//end if + }//for + ProtocolParameter[] result=new ProtocolParameter[v.size()]; + v.copyInto(result); + return result; + } + catch(Exception x) { + IOException tmp=new IOException(); + tmp.initCause(x); + throw tmp; + } + } + + public static void main(String[] args) throws Exception { + String input_file=null; + XmlConfigurator conf; + boolean old_format=false; + + for(int i=0; i < args.length; i++) { + if(args[i].equals("-old")) { + old_format=true; + continue; + } + if(args[i].equals("-file")) { + input_file=args[++i]; + continue; + } + help(); + return; + } + + if(input_file != null) { + InputStream input=null; + + try { + input=new FileInputStream(new File(input_file)); + } + catch(Throwable t) { + } + if(input == null) { + try { + input=new URL(input_file).openStream(); + } + catch(Throwable t) { + } + } + + if(input == null) + input=Thread.currentThread().getContextClassLoader().getResourceAsStream(input_file); + + if(old_format) { + Configurator config=new Configurator(); + String cfg=inputAsString(input); + Vector tmp=config.parseConfigurations(cfg); + System.out.println(dump(tmp)); + + +// conf=XmlConfigurator.getInstanceOldFormat(input); +// output=conf.getProtocolStackString(true); +// output=replace(output, "org.jgroups.protocols.", ""); +// System.out.println(getTitle(input_file)); +// System.out.println('\n' + output); + } + else { + conf=XmlConfigurator.getInstance(input); + String tmp=conf.getProtocolStackString(); + System.out.println("\n" + tmp); + } + } + else { + log.error("no input file given"); + } + } + + /** + * + * @param tmp Vector of Configurator.ProtocolConfiguration + * @return String (XML format) + */ + private static String dump(Vector tmp) { + StringBuilder sb=new StringBuilder(); + String indent=" "; + sb.append("\n"); + + for(Iterator it=tmp.iterator(); it.hasNext();) { + Configurator.ProtocolConfiguration cfg=(Configurator.ProtocolConfiguration)it.next(); + sb.append(indent).append("<").append(cfg.getProtocolName()); + Properties props=cfg.getProperties(); + if(props.isEmpty()) { + sb.append(" />\n"); + } + else { + sb.append("\n").append(indent).append(indent); + for(Iterator it2=props.entrySet().iterator(); it2.hasNext();) { + Map.Entry entry=(Map.Entry)it2.next(); + String key=(String)entry.getKey(); + String val=(String)entry.getValue(); + key=trim(key); + val=trim(val); + sb.append(key).append("=\"").append(val).append("\""); + if(it2.hasNext()) { + sb.append("\n").append(indent).append(indent); + } + } + sb.append(" />\n"); + } + } + + sb.append("\n"); + return sb.toString(); + } + + private static String trim(String val) { + String retval=""; + int index; + + val=val.trim(); + while(true) { + index=val.indexOf('\n'); + if(index == -1) { + retval+=val; + break; + } + retval+=val.substring(0, index); + val=val.substring(index+1); + } + + return retval; + } + + private static String inputAsString(InputStream input) throws IOException { + int len=input.available(); + byte[] buf=new byte[len]; + input.read(buf, 0, len); + return new String(buf); + } + + + public static String replace(String input, final String expr, String replacement) { + StringBuilder sb=new StringBuilder(); + int new_index=0, index=0, len=expr.length(), input_len=input.length(); + + while(true) { + new_index=input.indexOf(expr, index); + if(new_index == -1) { + sb.append(input.substring(index, input_len)); + break; + } + sb.append(input.substring(index, new_index)); + sb.append(replacement); + index=new_index + len; + } + + + return sb.toString(); + } + + + static void help() { + System.out.println("XmlConfigurator -file [-old]"); + System.out.println("(-old: converts old (plain-text) input format into new XML format)"); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/conf/package.html =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/conf/package.html,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/conf/package.html 17 Aug 2012 14:51:26 -0000 1.1 @@ -0,0 +1,5 @@ + + + Provides ways to configure a protocol stack. + + \ No newline at end of file Index: 3rdParty_sources/jgroups/org/jgroups/debug/Profiler.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/debug/Profiler.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/debug/Profiler.java 17 Aug 2012 14:51:26 -0000 1.1 @@ -0,0 +1,153 @@ +// $Id: Profiler.java,v 1.1 2012/08/17 14:51:26 marcin Exp $ + +package org.jgroups.debug; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.FileOutputStream; +import java.io.OutputStream; +import java.util.Enumeration; +import java.util.Hashtable; + +/** + * Allows to time execution of 'named' statements, counts number of times called and total + * execution time. + * + * @author Bela Ban + */ +public class Profiler { + + static public class Entry { + long num_calls=0; + long tot_time=0; + double avg=0.0; + long start_time=0; + long stop_time=0; + + synchronized void compute() { + num_calls++; + tot_time+=stop_time - start_time; + avg=(double)tot_time / num_calls; + } + } + + + private static OutputStream os=null; + private static final Hashtable entries=new Hashtable(); + private static Log log=LogFactory.getLog(Profiler.class); + + + public Profiler() { + try { + os=new FileOutputStream("profiler.dat"); + } + catch(Exception e) { + log.error(e); + } + } + + + public static void setFilename(String filename) { + try { + if(os != null) { + os.close(); + } + os=new FileOutputStream(filename); + } + catch(Exception e) { + log.error(e); + } + } + + + public static void start(String call_name) { + Entry e=(Entry)entries.get(call_name); + if(e == null) { + e=new Entry(); + entries.put(call_name, e); + } + e.start_time=System.currentTimeMillis(); + } + + + public static void stop(String call_name) { + Entry e=(Entry)entries.get(call_name); + if(e == null) { + log.error("Profiler.stop(): entry for " + call_name + " not found"); + return; + } + e.stop_time=System.currentTimeMillis(); + e.compute(); + } + + + public static void dump() { // dump to file + String key; + Entry val; + if(os == null) { + log.error("Profiler.dump(): output file is null"); + return; + } + try { + os.write("Key: Number of calls: Total time (ms): Average time (ms):\n".getBytes()); + os.write("-----------------------------------------------------------------\n\n".getBytes()); + } + catch(Exception e) { + log.error(e); + } + for(Enumeration e=entries.keys(); e.hasMoreElements();) { + key=(String)e.nextElement(); + val=(Entry)entries.get(key); + try { + os.write((key + ": " + val.num_calls + ' ' + + val.tot_time + ' ' + trim(val.avg) + '\n').getBytes()); + } + catch(Exception ex) { + log.error(ex); + } + } + } + + + public static double trim(double inp) { + double retval=0.0, rem=0.0; + long l1, l2; + + l1=(long)inp; + rem=inp - l1; + rem=rem * 100.0; + l2=(long)rem; + rem=l2 / 100.0; + retval=l1 + rem; + return retval; + } + + + public static void main(String[] args) { + Profiler.setFilename("bela.out"); + + + try { + + Profiler.start("time1"); + Thread.sleep(1500); + Profiler.stop("time1"); + + Profiler.start("time1"); + Thread.sleep(1500); + Profiler.start("time2"); + Thread.sleep(500); + + Profiler.stop("time2"); + Thread.sleep(1500); + Profiler.stop("time1"); + + + Profiler.dump(); + } + catch(Exception e) { + log.error(e); + } + } +} Index: 3rdParty_sources/jgroups/org/jgroups/debug/ProtocolTester.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/debug/ProtocolTester.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/debug/ProtocolTester.java 17 Aug 2012 14:51:26 -0000 1.1 @@ -0,0 +1,184 @@ +// $Id: ProtocolTester.java,v 1.1 2012/08/17 14:51:26 marcin Exp $ + +package org.jgroups.debug; + + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.Event; +import org.jgroups.Message; +import org.jgroups.stack.Configurator; +import org.jgroups.stack.Protocol; +import org.jgroups.stack.ProtocolStack; +import org.jgroups.util.Util; + +import java.util.LinkedList; +import java.util.List; +import java.util.Vector; + + +/** + * Generic class to test one or more protocol layers directly. Sets up a protocol stack consisting of + * the top layer (which is essentially given by the user and is the test harness), the specified protocol(s) and + * a bottom layer (which is automatically added), which sends all received messages immediately back up the + * stack (swapping sender and receiver address of the message). + * + * @author Bela Ban + * @author March 23 2001 + */ +public class ProtocolTester { + Protocol harness=null, top, bottom; + String props=null; + Configurator config=null; + + protected final Log log=LogFactory.getLog(this.getClass()); + + + public ProtocolTester(String prot_spec, Protocol harness) throws Exception { + if(prot_spec == null || harness == null) + throw new Exception("ProtocolTester(): prot_spec or harness is null"); + + props=prot_spec; + this.harness=harness; + props="LOOPBACK:" + props; // add a loopback layer at the bottom of the stack + + config=new Configurator(); + ProtocolStack stack=new ProtocolStack(); + top=Configurator.setupProtocolStack(props, stack); + harness.setDownProtocol(top); + top.setUpProtocol(harness); // +++ + + Configurator.initProtocolStack(getProtocols()); + + bottom=getBottomProtocol(top); + + // has to be set after StartProtocolStack, otherwise the up and down handler threads in the harness + // will be started as well (we don't want that) ! + // top.setUpProtocol(harness); + } + + public Vector getProtocols() { + Vector retval=new Vector(); + Protocol tmp=top; + while(tmp != null) { + retval.add(tmp); + tmp=tmp.getDownProtocol(); + } + return retval; + } + + public String getProtocolSpec() { + return props; + } + + + public Protocol getBottom() { + return bottom; + } + + public Protocol getTop() { + return top; + } + + public void start() throws Exception { + Protocol p; + if(harness != null) { + p=harness; + while(p != null) { + p.start(); + p=p.getDownProtocol(); + } + } + else if(top != null) { + p=top; + while(p != null) { + p.start(); + p=p.getDownProtocol(); + } + } + } + + public void stop() { + Protocol p; + if(harness != null) { + List protocols=new LinkedList(); + p=harness; + while(p != null) { + protocols.add(p); + p.stop(); + p=p.getDownProtocol(); + } + Configurator.destroyProtocolStack(protocols); + } + else if(top != null) { + p=top; + List protocols=new LinkedList(); + while(p != null) { + protocols.add(p); + p.stop(); + p=p.getDownProtocol(); + } + Configurator.destroyProtocolStack(protocols); + } + } + + + private final Protocol getBottomProtocol(Protocol top) { + Protocol tmp; + + if(top == null) + return null; + + tmp=top; + while(tmp.getDownProtocol() != null) + tmp=tmp.getDownProtocol(); + return tmp; + } + + + public static void main(String[] args) { + String props; + ProtocolTester t; + Harness h; + + if(args.length < 1 || args.length > 2) { + System.out.println("ProtocolTester [-trace]"); + return; + } + props=args[0]; + + try { + h=new Harness(); + t=new ProtocolTester(props, h); + System.out.println("protocol specification is " + t.getProtocolSpec()); + h.down(new Event(Event.BECOME_SERVER)); + for(int i=0; i < 5; i++) { + System.out.println("Sending msg #" + i); + h.down(new Event(Event.MSG, new Message(null, null, "Hello world #" + i))); + } + Util.sleep(500); + t.stop(); + } + catch(Exception ex) { + System.err.println(ex); + } + } + + + + + private static class Harness extends Protocol { + + public String getName() { + return "Harness"; + } + + + public Object up(Event evt) { + System.out.println("Harness.up(): " + evt); + return null; + } + + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/debug/Simulator.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/debug/Simulator.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/debug/Simulator.java 17 Aug 2012 14:51:26 -0000 1.1 @@ -0,0 +1,276 @@ +package org.jgroups.debug; + +import org.jgroups.Address; +import org.jgroups.ChannelException; +import org.jgroups.Event; +import org.jgroups.Message; +import org.jgroups.View; +import org.jgroups.protocols.TP; +import org.jgroups.stack.Protocol; +import org.jgroups.stack.ProtocolStack; +import org.jgroups.util.Queue; +import org.jgroups.util.QueueClosedException; +import org.jgroups.util.TimeScheduler; + +import java.util.HashMap; +import java.util.Iterator; + +/** + * Tests one or more protocols independently. Look at org.jgroups.tests.FCTest for an example of how to use it. + * @author Bela Ban + * @version $Id: Simulator.java,v 1.1 2012/08/17 14:51:26 marcin Exp $ + */ +public class Simulator { + private Protocol[] protStack=null; + private ProtocolAdapter ad=new ProtocolAdapter(); + ProtocolStack prot_stack=null; + private Receiver r=null; + private Protocol top=null, bottom=null; + private Queue send_queue=new Queue(); + private Thread send_thread; + private Queue recv_queue=new Queue(); + private Thread recv_thread; + + + /** HashMap from Address to Simulator. */ + private final HashMap addrTable=new HashMap(); + private Address local_addr=null; + private View view; + + public interface Receiver { + void receive(Event evt); + } + + + public void setProtocolStack(Protocol[] stack) { + this.protStack=stack; + this.protStack[0].setUpProtocol(ad); + this.protStack[this.protStack.length-1].setDownProtocol(ad); + top=protStack[0]; + bottom=this.protStack[this.protStack.length-1]; + + try { + prot_stack=new ProtocolStack(); + } catch (ChannelException e) { + e.printStackTrace(); + } + + if(protStack.length > 1) { + for(int i=0; i < protStack.length; i++) { + Protocol p1=protStack[i]; + p1.setProtocolStack(prot_stack); + Protocol p2=i+1 >= protStack.length? null : protStack[i+1]; + if(p2 != null) { + p1.setDownProtocol(p2); + p2.setUpProtocol(p1); + } + } + } + } + + public String dumpStats() { + StringBuilder sb=new StringBuilder(); + for(int i=0; i < protStack.length; i++) { + Protocol p1=protStack[i]; + sb.append(p1.getName()).append(":\n").append(p1.dumpStats()).append("\n"); + } + return sb.toString(); + } + + public void addMember(Address addr) { + addMember(addr, this); + } + + public void addMember(Address addr, Simulator s) { + addrTable.put(addr, s); + } + + public void setLocalAddress(Address addr) { + this.local_addr=addr; + } + + public void setView(View v) { + this.view=v; + } + + public void setReceiver(Receiver r) { + this.r=r; + } + + public Object send(Event evt) { + return top.down(evt); + } + + public void receive(Event evt) { + try { + Event copy; + if(evt.getType() == Event.MSG && evt.getArg() != null) { + copy=new Event(Event.MSG, ((Message)evt.getArg()).copy()); + } + else + copy=evt; + + recv_queue.add(copy); + } + catch(QueueClosedException e) { + } + } + + public void start() throws Exception { + if(local_addr == null) + throw new Exception("local_addr has to be non-null"); + if(protStack == null) + throw new Exception("protocol stack is null"); + + bottom.up(new Event(Event.SET_LOCAL_ADDRESS, local_addr)); + if(view != null) { + Event view_evt=new Event(Event.VIEW_CHANGE, view); + bottom.up(view_evt); + top.down(view_evt); + } + + for(int i=0; i < protStack.length; i++) { + Protocol p=protStack[i]; + p.setProtocolStack(prot_stack); + } + + for(int i=0; i < protStack.length; i++) { + Protocol p=protStack[i]; + p.init(); + } + + for(int i=0; i < protStack.length; i++) { + Protocol p=protStack[i]; + p.start(); + } + + + send_thread=new Thread() { + public void run() { + Event evt; + while(send_thread != null) { + try { + evt=(Event)send_queue.remove(); + if(evt.getType() == Event.MSG) { + Message msg=(Message)evt.getArg(); + Address dst=msg.getDest(); + if(msg.getSrc() == null) + ((Message)evt.getArg()).setSrc(local_addr); + Simulator s; + if(dst == null) { + for(Iterator it=addrTable.values().iterator(); it.hasNext();) { + s=(Simulator)it.next(); + s.receive(evt); + } + } + else { + s=(Simulator)addrTable.get(dst); + if(s != null) + s.receive(evt); + } + } + } + catch(QueueClosedException e) { + send_thread=null; + break; + } + } + } + }; + send_thread.start(); + + + recv_thread=new Thread() { + public void run() { + Event evt; + while(recv_thread != null) { + try { + evt=(Event)recv_queue.remove(); + bottom.up(evt); + } + catch(QueueClosedException e) { + recv_thread=null; + break; + } + } + } + }; + recv_thread.start(); + } + + public void stop() { + recv_thread=null; + recv_queue.close(false); + send_thread=null; + send_queue.close(false); + if(ad != null) { + try { + ad.getTimer().stop(); + } + catch(InterruptedException e) { + e.printStackTrace(); + } + } + } + + + + + class ProtocolAdapter extends TP { + + ProtocolAdapter() { + timer=new TimeScheduler(); + } + + public TimeScheduler getTimer() { + return timer; + } + + public void setTimer(TimeScheduler timer) { + this.timer=timer; + } + + public String getName() { + return "ProtocolAdapter"; + } + + public void sendToAllMembers(byte[] data, int offset, int length) throws Exception { + } + + public void sendToSingleMember(Address dest, byte[] data, int offset, int length) throws Exception { + } + + public String getInfo() { + return null; + } + + public void postUnmarshalling(Message msg, Address dest, Address src, boolean multicast) { + } + + public void postUnmarshallingList(Message msg, Address dest, boolean multicast) { + } + + public void init() throws Exception { + super.init(); + } + + public Object up(Event evt) { + if(r != null) + r.receive(evt); + return null; + } + + /** send to unicast or multicast destination */ + public Object down(Event evt) { + try { + send_queue.add(evt); + } + catch(QueueClosedException e) { + } + return null; + } + } + + + +} Index: 3rdParty_sources/jgroups/org/jgroups/debug/package.html =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/debug/package.html,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/debug/package.html 17 Aug 2012 14:51:26 -0000 1.1 @@ -0,0 +1,5 @@ + + + Provides debug support, including testing, profiling, and a graphical view of a protocol stack. + + \ No newline at end of file Index: 3rdParty_sources/jgroups/org/jgroups/demos/CausalDemo.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/demos/CausalDemo.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/demos/CausalDemo.java 17 Aug 2012 14:51:24 -0000 1.1 @@ -0,0 +1,201 @@ +// $Id: CausalDemo.java,v 1.1 2012/08/17 14:51:24 marcin Exp $ +package org.jgroups.demos; + +import org.jgroups.*; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.Serializable; +import java.util.Random; +import java.util.Vector; + + + +/** + * Simple causal demo where each member bcast a consecutive letter from the + * alphabet and picks the next member to transmit the next letter. Start a + * few instances of CausalDemo and pass a paramter "-start" to a CausalDemo + * that initiates transmission of a letter A. All participanting members should + * have correct alphabet. DISCARD layer has been added to simulate lost messages, + * thus forcing delaying of delivery of a certain alphabet letter until the causally + * prior one has been received. Remove CAUSAL from the stack and witness how FIFO + * alone doesn't provide this guarantee. + * + * @author Vladimir Blagojevic + */ +public class CausalDemo implements Runnable +{ + private Channel channel; + private final Vector alphabet = new Vector(); + private boolean starter = false; + private int doneCount=0; + private Log log=LogFactory.getLog(getClass()); + + private final String props = "causal.xml"; + + public CausalDemo(boolean start) + { + starter = start; + } + + public String getNext(String c) + { + char letter = c.charAt(0); + return new String(new char[]{++letter}); + } + + public void listAlphabet() + { + System.out.println(alphabet); + } + + public void run() + { + Object obj; + Message msg; + Random r = new Random(); + + try + { + channel = new JChannel(props); + channel.connect("CausalGroup"); + System.out.println("View:" + channel.getView()); + if (starter) + channel.send(new Message(null, null, new CausalMessage("A", (Address) channel.getView().getMembers().get(0)))); + + } + catch (Exception e) + { + System.out.println("Could not conec to channel"); + } + + try + { + Runtime.getRuntime().addShutdownHook( + new Thread("Shutdown cleanup thread") + { + public void run() + { + + listAlphabet(); + channel.disconnect(); + channel.close(); + } + } + ); + } + catch (Exception e) + { + System.out.println("Exception while shutting down" + e); + } + + while (true) + { + try + { + CausalMessage cm = null; + obj = channel.receive(0); // no timeout + if (obj instanceof Message) + { + msg = (Message) obj; + cm = (CausalMessage) msg.getObject(); + Vector members = channel.getView().getMembers(); + String receivedLetter = cm.message; + + if("Z".equals(receivedLetter)) + { + channel.send(new Message(null, null, new CausalMessage("done", null))); + } + if("done".equals(receivedLetter)) + { + if(++doneCount >= members.size()) + { + System.exit(0); + } + continue; + } + + alphabet.add(receivedLetter); + listAlphabet(); + + //am I chosen to transmit next letter? + if (cm.member.equals(channel.getLocalAddress())) + { + int nextTarget = r.nextInt(members.size()); + + //chose someone other than yourself + while (nextTarget == members.indexOf(channel.getLocalAddress())) + { + nextTarget = r.nextInt(members.size()); + } + Address next = (Address) members.get(nextTarget); + String nextChar = getNext(receivedLetter); + if (nextChar.compareTo("Z") < 1) + { + System.out.println("Sending " + nextChar); + channel.send(new Message(null, null, new CausalMessage(nextChar, next))); + } + } + } + } + catch (ChannelNotConnectedException conn) + { + break; + } + catch (Exception e) + { + log.error(e); + } + } + + } + + + public static void main(String args[]) + { + CausalDemo test = null; + boolean start=false; + + for(int i=0; i < args.length; i++) { + if("-help".equals(args[i])) { + System.out.println("CausalDemo [-help] [-start]"); + return; + } + if("-start".equals(args[i])) { + start=true; + continue; + } + } + + //if parameter start is passed , start the demo + test = new CausalDemo(start); + try + { + new Thread(test).start(); + } + catch (Exception e) + { + System.err.println(e); + } + + } + +} + +class CausalMessage implements Serializable +{ + public final String message; + public final Address member; + + public CausalMessage(String message, Address member) + { + this.message = message; + this.member = member; + } + + public String toString() + { + return "CausalMessage[" + message + '=' + message + "member=" + member + ']'; + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/demos/Chat.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/demos/Chat.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/demos/Chat.java 17 Aug 2012 14:51:23 -0000 1.1 @@ -0,0 +1,152 @@ +package org.jgroups.demos; + +import java.awt.Frame; +import java.awt.Label; +import java.awt.Rectangle; +import java.awt.TextArea; +import java.awt.TextField; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; + +import javax.swing.JButton; + +public class Chat extends ChatCore implements MouseListener, WindowListener { + + Frame mainFrame; + + TextArea txtArea; + + TextField txtField; + + Label csLabel; + + JButton leaveButton; + + JButton sendButton; + + JButton clearButton; + + public Chat(String props) { + super(props); + } + + public static void main(String[] args) { + String props = null; + + for (int i = 0; i < args.length; i++) { + if ("-props".equals(args[i])) { + props = args[++i]; + continue; + } + help(); + return; + } + + Chat instance = new Chat(props); + instance.start(); + } + + void post(String msg) { + txtArea.append(msg); + } + + static void help() { + System.out.println("Chat [-help] [-props ]"); + } + + public void start() { + mainFrame = new Frame(); + mainFrame.setLayout(null); + mainFrame.setSize(600, 507); + mainFrame.addWindowListener(this); + + txtArea = new TextArea(); + txtArea.setBounds(12, 36, 550, 348); + txtArea.setEditable(false); + mainFrame.add(txtArea); + + txtField = new TextField(); + txtField.setBounds(100, 392, 400, 30); + mainFrame.add(txtField); + + csLabel = new Label("Send:"); + csLabel.setBounds(12, 392, 85, 30); + mainFrame.add(csLabel); + + leaveButton = new JButton("Leave"); + leaveButton.setBounds(12, 428, 150, 30); + leaveButton.addMouseListener(this); + mainFrame.add(leaveButton); + + sendButton = new JButton("Send"); + sendButton.setBounds(182, 428, 150, 30); + sendButton.addMouseListener(this); + mainFrame.add(sendButton); + + clearButton = new JButton("Clear"); + clearButton.setBounds(340, 428, 150, 30); + clearButton.addMouseListener(this); + mainFrame.add(clearButton); + + link(); + + mainFrame.pack(); + mainFrame.setLocation(15, 25); + mainFrame.setBounds(new Rectangle(580, 480)); + mainFrame.setVisible(true); + mainFrame.show(); + + dumpHist(); + + } + + public void mouseClicked(MouseEvent e) { + Object obj = e.getSource(); + + if (obj == leaveButton) { + handleLeave(); + System.exit(0); + } else if (obj == sendButton) + handleSend(txtField.getText()); + else if (obj == clearButton) + txtArea.setText(""); + } + + public void mouseEntered(MouseEvent e) { + } + + public void mouseExited(MouseEvent e) { + } + + public void mousePressed(MouseEvent e) { + } + + public void mouseReleased(MouseEvent e) { + } + + public void windowActivated(WindowEvent e) { + } + + public void windowClosed(WindowEvent e) { + } + + public void windowClosing(WindowEvent e) { + handleLeave(); + System.exit(0); + } + + public void windowDeactivated(WindowEvent e) { + } + + public void windowDeiconified(WindowEvent e) { + } + + public void windowIconified(WindowEvent e) { + } + + public void windowOpened(WindowEvent e) { + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/demos/ChatCore.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/demos/ChatCore.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/demos/ChatCore.java 17 Aug 2012 14:51:23 -0000 1.1 @@ -0,0 +1,165 @@ +package org.jgroups.demos; + +// prepend 'bridge.' to the next 2 imports if +// you want to use this class w/ JGroups-ME +import java.util.Iterator; +import java.util.LinkedList; + +import org.jgroups.Address; +import org.jgroups.Channel; +import org.jgroups.JChannel; +import org.jgroups.MembershipListener; +import org.jgroups.Message; +import org.jgroups.MessageListener; +import org.jgroups.View; +import org.jgroups.blocks.PullPushAdapter; +import org.jgroups.util.Util; + +public abstract class ChatCore implements MessageListener, MembershipListener { + Channel channel; + + PullPushAdapter ad; + + Thread mainThread; + + static final String group_name = "ChatGroup"; + + String props = null; + + String username = null; + + LinkedList history = new LinkedList(); + + public ChatCore(String props) { + this.props = props; + try { + username = System.getProperty("user.name"); + } catch (Throwable t) { + } + } + + abstract void post(String msg); + + public void link() { + + try { + channel = new JChannel(props); + channel.setOpt(Channel.AUTO_RECONNECT, Boolean.TRUE); + channel.setOpt(Channel.AUTO_GETSTATE, Boolean.TRUE); + System.out.println("Connecting to " + group_name); + channel.connect(group_name); + ad = new PullPushAdapter(channel, this, this); + channel.getState(null, 5000); + } catch (Exception e) { + e.printStackTrace(); + post(e.toString()); + } + + } + + public void dumpHist() { + + if (!history.isEmpty()) { + for (Iterator it = history.iterator(); it.hasNext();) { + String s = (String) it.next(); + post(s + "\n"); + System.err.print(s + "\n"); + } + } + } + + /* -------------------- Interface MessageListener ------------------- */ + + public void receive(Message msg) { + String o; + + try { + o = new String(msg.getBuffer()); + System.err.print(o + " [" + msg.getSrc() + "]\n"); + post(o + " [" + msg.getSrc() + "]\n"); + history.add(o); + } catch (Exception e) { + post("Chat.receive(): " + e); + System.err.print("Chat.receive(): " + e); + } + } + + public byte[] getState() { + try { + return Util.objectToByteBuffer(history); + } catch (Exception e) { + return null; + } + } + + public void setState(byte[] state) { + try { + history = (LinkedList) Util.objectFromByteBuffer(state); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /* ----------------- End of Interface MessageListener --------------- */ + + /* ------------------- Interface MembershipListener ----------------- */ + + public void viewAccepted(View new_view) { + post("Received view " + new_view + '\n'); + System.err.print("Received view " + new_view + '\n'); + } + + public void suspect(Address suspected_mbr) { + } + + public void block() { + } + + public void stop() { + System.out.print("Stopping PullPushAdapter"); + ad.stop(); + System.out.println(" -- done"); + } + + public void disconnect() { + System.out.print("Disconnecting the channel"); + channel.disconnect(); + System.out.println(" -- done"); + } + + public void close() { + System.out.print("Closing the channel"); + channel.close(); + System.out.println(" -- done"); + } + + /* --------------- End of Interface MembershipListener -------------- */ + + protected synchronized void handleLeave() { + try { + stop(); + disconnect(); + close(); + } catch (Exception e) { + e.printStackTrace(); + System.err + .print("Failed leaving the group: " + e.toString() + '\n'); + post("Failed leaving the group: " + e.toString() + '\n'); + } + } + + protected void handleSend(String txt) { + String tmp = username + ": " + txt; + try { + // for the sake of jgroups-me compatibility we stick with + // byte buffers and Streamable *only* (not Serializable) + Message msg = new Message(null, null, tmp.getBytes(), 0, tmp + .length()); + channel.send(msg); + } catch (Exception e) { + System.err.print("Failed sending message: " + e.toString() + '\n'); + post("Failed sending message: " + e.toString() + '\n'); + } + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/demos/DistributedHashtableDemo.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/demos/DistributedHashtableDemo.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/demos/DistributedHashtableDemo.java 17 Aug 2012 14:51:23 -0000 1.1 @@ -0,0 +1,297 @@ +// $Id: DistributedHashtableDemo.java,v 1.1 2012/08/17 14:51:23 marcin Exp $ + + +package org.jgroups.demos; + + +import org.jgroups.ChannelException; +import org.jgroups.ChannelFactory; +import org.jgroups.JChannelFactory; +import org.jgroups.blocks.DistributedHashtable; +import org.jgroups.persistence.PersistenceFactory; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; +import java.util.Enumeration; +import java.util.Map; +import java.util.Vector; + + + + +/** + * Uses the DistributedHashtable building block. The latter subclasses java.util.Hashtable and overrides + * the methods that modify the hashtable (e.g. put()). Those methods are multicast to the group, whereas + * read-only methods such as get() use the local copy. A DistributedHashtable is created given the name + * of a group; all hashtables with the same name find each other and form a group. + * @author Bela Ban + */ +public class DistributedHashtableDemo extends Frame implements WindowListener, ActionListener, + DistributedHashtable.Notification { + static final String groupname="HashDemo"; + DistributedHashtable h=null; + final JButton get=new JButton("Get"); + final JButton set=new JButton("Set"); + final JButton quit=new JButton("Quit"); + final JButton get_all=new JButton("All"); + final JButton delete=new JButton("Delete"); + final JLabel stock=new JLabel("Key"); + final JLabel value=new JLabel("Value"); + final JLabel err_msg=new JLabel("Error"); + final JTextField stock_field=new JTextField(); + final JTextField value_field=new JTextField(); + final java.awt.List listbox=new java.awt.List(); + final Font default_font=new Font("Helvetica", Font.PLAIN,12); + + + + + public DistributedHashtableDemo() { + super(); + addWindowListener(this); + } + + private void showMsg(String msg) { + err_msg.setText(msg); + err_msg.setVisible(true); + } + + private void clearMsg() {err_msg.setVisible(false);} + + + private void removeItem() { + int index=listbox.getSelectedIndex(); + if(index == -1) { + showMsg("No item selected in listbox to be deleted !"); + return; + } + String s=listbox.getSelectedItem(); + String key=s.substring(0, s.indexOf(':', 0)); + if(key != null) + h.remove(key); + } + + private void showAll() { + if(listbox.getItemCount() > 0) + listbox.removeAll(); + if(h.size() == 0) + return; + clearMsg(); + String key; + Float val; + + for(Enumeration en=h.keys(); en.hasMoreElements();) { + key=(String)en.nextElement(); + val=(Float)h.get(key); + if(val == null) + continue; + listbox.add(key + ": " + val.toString()); + } + } + + + + + + public void start(ChannelFactory factory, String props, boolean persist) + throws ChannelException { + h=new DistributedHashtable(groupname, factory, props, persist, 10000); + h.addNotifier(this); + + setLayout(null); + setSize(400, 300); + setFont(default_font); + + stock.setBounds(new Rectangle(10, 30, 60, 30)); + value.setBounds(new Rectangle(10, 60, 60, 30)); + stock_field.setBounds(new Rectangle(100, 30, 100, 30)); + value_field.setBounds(new Rectangle(100, 60, 100, 30)); + listbox.setBounds(new Rectangle(210, 30, 150, 160)); + err_msg.setBounds(new Rectangle(10, 200, 350, 30)); + err_msg.setFont(new Font("Helvetica",Font.ITALIC,12)); + err_msg.setForeground(Color.red); + err_msg.setVisible(false); + get.setBounds(new Rectangle(10, 250, 60, 30)); + set.setBounds(new Rectangle(80, 250, 60, 30)); + quit.setBounds(new Rectangle(150, 250, 60, 30)); + get_all.setBounds(new Rectangle(220, 250, 60, 30)); + delete.setBounds(new Rectangle(290, 250, 80, 30)); + + get.addActionListener(this); + set.addActionListener(this); + quit.addActionListener(this); + get_all.addActionListener(this); + delete.addActionListener(this); + + add(stock); add(value); + add(stock_field); add(value_field); + add(err_msg); + add(get); add(set); add(quit); add(get_all); add(delete); + add(listbox); + setTitle("DistributedHashtable Demo"); + showAll(); + // pack(); + setVisible(true); + + +// new Thread() { +// public void run() { +// System.out.println("-- sleeping"); +// Util.sleep(10000); +// for(int i=0; i < 10; i++) { +// System.out.println("-- put()"); +// h.put("Bela#" + i, new Float(i)); +// } + +// while(true) { +// Util.sleep(500); +// System.out.println(h.get("Bela#1")); +// } + +// } +// }.start(); + + + + } + + + + + public void windowActivated(WindowEvent e) {} + public void windowClosed(WindowEvent e) {} + public void windowClosing(WindowEvent e) {System.exit(0);} + public void windowDeactivated(WindowEvent e) {} + public void windowDeiconified(WindowEvent e) {} + public void windowIconified(WindowEvent e) {} + public void windowOpened(WindowEvent e) {} + + + public void actionPerformed(ActionEvent e) { + String command=e.getActionCommand(); + try { + if(command == "Get") { + String stock_name=stock_field.getText(); + if(stock_name == null || stock_name.length() == 0) { + showMsg("Key is empty !"); + return; + } + showMsg("Looking up value for " + stock_name + ':'); + Float val=(Float)h.get(stock_name); + if(val != null) { + value_field.setText(val.toString()); + clearMsg(); + } + else { + value_field.setText(""); + showMsg("Value for " + stock_name + " not found"); + } + } + else if(command == "Set") { + String stock_name=stock_field.getText(); + String stock_val=value_field.getText(); + if(stock_name == null || stock_val == null || stock_name.length() == 0 || + stock_val.length() == 0) { + showMsg("Both key and value have to be present to create a new entry"); + return; + } + Float val=new Float(stock_val); + h.put(stock_name, val); + showMsg("Key " + stock_name + " set to " + val); + } + else if(command == "All") { + showAll(); + } + else if(command == "Quit") { + setVisible(false); + System.exit(0); + } + else if(command == "Delete") + removeItem(); + else + System.out.println("Unknown action"); + } + catch(Exception ex) { + value_field.setText(""); + showMsg(ex.toString()); + } + } + + + + public void entrySet(Object key, Object value) {showAll();} + + public void entryRemoved(Object key) {showAll();} + + public void viewChange(Vector joined, Vector left) { + System.out.println("New members: " + joined + ", left members: " + left); + } + + public void contentsSet(Map m) { + System.out.println("new contents: " + m); + } + + public void contentsCleared() { + System.out.println("contents cleared"); + } + + + public static void main(String args[]) { + DistributedHashtableDemo client=new DistributedHashtableDemo(); + ChannelFactory factory=new JChannelFactory(); + String arg; + boolean persist=false; + + // test for pbcast + /* + String props="UDP:" + + "PING(num_initial_members=2;timeout=3000):" + + "FD:" + + // "DISCARD(down=0.1):" + // this is for discarding of 10% of the up messages ! + "pbcast.PBCAST(gossip_interval=5000;gc_lag=50):" + + "UNICAST:" + + "FRAG:" + + "pbcast.GMS:" + + "pbcast.STATE_TRANSFER"; + */ + + String props="udp.xml"; + + try { + for(int i=0; i < args.length; i++) { + arg=args[i]; + if("-persist".equals(arg) && i+1]"); + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/demos/DistributedQueueDemo.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/demos/DistributedQueueDemo.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/demos/DistributedQueueDemo.java 17 Aug 2012 14:51:24 -0000 1.1 @@ -0,0 +1,307 @@ +// $Id: DistributedQueueDemo.java,v 1.1 2012/08/17 14:51:24 marcin Exp $ +package org.jgroups.demos; + +import org.jgroups.ChannelException; +import org.jgroups.ChannelFactory; +import org.jgroups.JChannelFactory; +import org.jgroups.blocks.DistributedQueue; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; +import java.util.Collection; +import java.util.Vector; + + +/** + * Uses the DistributedQueue building block. The latter subclasses org.jgroups.util.Queue and overrides + * the methods that modify the queue (e.g. add()). Those methods are multicast to the group, whereas + * read-only methods such as peek() use the local copy. A DistributedQueue is created given the name + * of a group; all queues with the same name find each other and form a group. + * @author Romuald du Song + */ +public class DistributedQueueDemo extends Frame implements WindowListener, ActionListener, + DistributedQueue.Notification +{ + DistributedQueue h = null; + final JButton add = new JButton("Add"); + final JButton quit = new JButton("Quit"); + final JButton get_all = new JButton("All"); + final JButton remove = new JButton("Remove"); + final JLabel value = new JLabel("Value"); + final JLabel err_msg = new JLabel("Error"); + final JTextField value_field = new JTextField(); + final java.awt.List listbox = new java.awt.List(); + final Font default_font = new Font("Helvetica", Font.PLAIN, 12); + + public DistributedQueueDemo() + { + super(); + addWindowListener(this); + } + + private void showMsg(String msg) + { + err_msg.setText(msg); + err_msg.setVisible(true); + } + + private void clearMsg() + { + err_msg.setVisible(false); + } + + private void removeItem() + { + h.remove(); + } + + private void showAll() + { + if (listbox.getItemCount() > 0) + { + listbox.removeAll(); + } + + if (h.size() == 0) + { + return; + } + + clearMsg(); + + String key; + + Vector v = h.getContents(); + + for (int i = 0; i < v.size(); i++) + { + listbox.add((String)v.elementAt(i)); + } + } + + public void start(String groupname, ChannelFactory factory, String props) + throws ChannelException + { + h = new DistributedQueue(groupname, factory, props, 10000); + h.addNotifier(this); + + setLayout(null); + setSize(400, 300); + setFont(default_font); + + value.setBounds(new Rectangle(10, 60, 60, 30)); + value_field.setBounds(new Rectangle(100, 60, 100, 30)); + listbox.setBounds(new Rectangle(210, 30, 150, 160)); + err_msg.setBounds(new Rectangle(10, 200, 350, 30)); + err_msg.setFont(new Font("Helvetica", Font.ITALIC, 12)); + err_msg.setForeground(Color.red); + err_msg.setVisible(false); + add.setBounds(new Rectangle(60, 250, 60, 30)); + quit.setBounds(new Rectangle(130, 250, 70, 30)); + get_all.setBounds(new Rectangle(210, 250, 60, 30)); + remove.setBounds(new Rectangle(280, 250, 90, 30)); + + add.addActionListener(this); + quit.addActionListener(this); + get_all.addActionListener(this); + remove.addActionListener(this); + + add(value); + add(value_field); + add(err_msg); + add(add); + add(quit); + add(get_all); + add(remove); + add(listbox); + setTitle("DistributedQueue Demo"); + showAll(); + // pack(); + setVisible(true); + + /* + new Thread() { + public void run() { + System.out.println("-- sleeping"); + Util.sleep(10000); + for(int i=0; i < 10; i++) { + System.out.println("-- add()"); + h.add("Bela#" + i); + } + + while(true) { + Util.sleep(500); + try + { + System.out.println(h.remove()); + } + catch (QueueClosedException e) + { + e.printStackTrace(); + } + } + + } + }.start(); + */ + } + + public void windowActivated(WindowEvent e) + { + } + + public void windowClosed(WindowEvent e) + { + } + + public void windowClosing(WindowEvent e) + { + System.exit(0); + } + + public void windowDeactivated(WindowEvent e) + { + } + + public void windowDeiconified(WindowEvent e) + { + } + + public void windowIconified(WindowEvent e) + { + } + + public void windowOpened(WindowEvent e) + { + } + + public void actionPerformed(ActionEvent e) + { + String command = e.getActionCommand(); + + try + { + if (command == "Add") + { + String value_name = value_field.getText(); + + if ((value_name == null) || (value_name.length() == 0)) + { + showMsg("Value is empty !"); + + return; + } + + showMsg("Adding value " + value_name + ':'); + h.add(value_name); + } + else if (command == "All") + { + showAll(); + } + else if (command == "Quit") + { + setVisible(false); + System.exit(0); + } + else if (command == "Remove") + { + removeItem(); + } + else + { + System.out.println("Unknown action"); + } + } + catch (Exception ex) + { + value_field.setText(""); + showMsg(ex.toString()); + } + } + + public void entryAdd(Object value) + { + showAll(); + } + + public void entryRemoved(Object key) + { + showAll(); + } + + public void viewChange(Vector joined, Vector left) + { + System.out.println("New members: " + joined + ", left members: " + left); + } + + public void contentsSet(Collection new_entries) + { + System.out.println("Contents Set:" + new_entries); + } + + public void contentsCleared() + { + System.out.println("Contents cleared()"); + } + + public static void main(String[] args) + { + String groupname = "QueueDemo"; + DistributedQueueDemo client = new DistributedQueueDemo(); + ChannelFactory factory = new JChannelFactory(); + String arg; + String next_arg; + boolean trace = false; + boolean persist = false; + + String props ="udp.xml"; + + try + { + for (int i = 0; i < args.length; i++) + { + arg = args[i]; + + if ("-trace".equals(arg)) + { + trace = true; + continue; + } + + if ("-groupname".equals(args[i])) + { + groupname = args[++i]; + continue; + } + + help(); + return; + } + } + catch (Exception e) + { + help(); + + return; + } + + try + { + client.start(groupname, factory, props); + } + catch (Throwable t) + { + t.printStackTrace(); + } + } + + static void help() + { + System.out.println("DistributedQueueDemo [-help]"); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/demos/DistributedTreeDemo.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/demos/DistributedTreeDemo.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/demos/DistributedTreeDemo.java 17 Aug 2012 14:51:24 -0000 1.1 @@ -0,0 +1,483 @@ +// $Id: DistributedTreeDemo.java,v 1.1 2012/08/17 14:51:24 marcin Exp $ + +package org.jgroups.demos; + + +import org.jgroups.blocks.DistributedTree; + +import javax.swing.*; +import javax.swing.event.TableModelEvent; +import javax.swing.event.TableModelListener; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; +import javax.swing.table.DefaultTableModel; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; +import java.awt.*; +import java.awt.event.*; +import java.io.Serializable; +import java.util.Enumeration; +import java.util.Properties; +import java.util.StringTokenizer; +import java.util.Vector; + + +class MyNode extends DefaultMutableTreeNode { + String name=""; + + + MyNode(String name) { + this.name=name; + } + + MyNode(String name, Serializable user_obj) { + super(user_obj); + this.name=name; + } + + + void add(String fqn) { + add(fqn, null); + } + + public void add(String fqn, Serializable user_obj) { + MyNode curr, n; + StringTokenizer tok; + String child_name; + + if(fqn == null) return; + curr=this; + tok=new StringTokenizer(fqn, "/"); + + while(tok.hasMoreTokens()) { + child_name=tok.nextToken(); + n=curr.findChild(child_name); + if(n == null) { + n=new MyNode(child_name, user_obj); + curr.add(n); + } + curr=n; + } + curr.userObject=user_obj; + } + + + void modify(String fqn, Serializable new_element) { + if(fqn == null || new_element == null) return; + MyNode n=findNode(fqn); + if(n != null) + n.userObject=new_element; + } + + + void remove(String fqn) { + System.out.println("MyNode.remove(" + fqn + ')'); + removeFromParent(); + } + + + public MyNode findNode(String fqn) { + MyNode curr, n; + StringTokenizer tok; + String child_name; + + if(fqn == null) return null; + curr=this; + tok=new StringTokenizer(fqn, "/"); + + while(tok.hasMoreTokens()) { + child_name=tok.nextToken(); + n=curr.findChild(child_name); + if(n == null) + return null; + curr=n; + } + return curr; + } + + + MyNode findChild(String relative_name) { + MyNode child; + + if(relative_name == null || getChildCount() == 0) + return null; + for(int i=0; i < getChildCount(); i++) { + child=(MyNode)getChildAt(i); + if(child.name == null) { + System.err.println("MyNode.findChild(" + relative_name + "): child.name is null"); + continue; + } + + if(child.name.equals(relative_name)) + return child; + } + return null; + } + + + String print(int indent) { + StringBuilder sb=new StringBuilder(); + + for(int i=0; i < indent; i++) + sb.append(' '); + if(!isRoot()) { + if(name == null) + sb.append("/"); + else { + sb.append('/' + name); + if(userObject != null) + sb.append(" --> " + userObject); + } + } + sb.append('\n'); + if(getChildCount() > 0) { + if(isRoot()) indent=0; + else indent+=4; + for(int i=0; i < getChildCount(); i++) + sb.append(((MyNode)getChildAt(i)).print(indent)); + } + return sb.toString(); + } + + + public String toString() { + return name; + } + + +} + + +/** + * Demo showing the DistributedTree class. It displays a panel with the tree structure in the upper half, + * and the properties of a chosen node on the bottom half. All updates are broadcast to all members. + */ +public class DistributedTreeDemo extends Frame implements WindowListener, + DistributedTree.DistributedTreeListener, + TreeSelectionListener, TableModelListener { + DefaultTreeModel tree_model=null; + JTree jtree=null; + final DefaultTableModel table_model=new DefaultTableModel(); + final JTable table=new JTable(table_model); + JScrollPane scroll_pane=null; + final MyNode root=new MyNode("/"); + DistributedTree dt=null; + String props=null; + String selected_node=null; + boolean create=false; + + + public DistributedTreeDemo(boolean create) throws Exception { + props="udp.xml"; + + + this.create=create; + dt=new DistributedTree("DistributedTreeDemo", props); + dt.addDistributedTreeListener(this); + setLayout(new BorderLayout()); + addNotify(); + setSize(getInsets().left + getInsets().right + 485, + getInsets().top + getInsets().bottom + 367); + setTitle("DistributedTree"); + + tree_model=new DefaultTreeModel(root); + jtree=new JTree(tree_model); + jtree.setDoubleBuffered(true); + + scroll_pane=new JScrollPane(); + scroll_pane.getViewport().add(jtree); + scroll_pane.setDoubleBuffered(true); + add(scroll_pane, BorderLayout.CENTER); + addWindowListener(this); + + table_model.setColumnIdentifiers(new String[]{"Name", "Value"}); + table_model.addTableModelListener(this); + add(table, BorderLayout.SOUTH); + + + dt.start(); + System.out.println("Constructing initial GUI tree"); + populateTree(dt, ""); // creates initial GUI from model + System.out.println("Constructing initial GUI tree -- done"); + + + Properties props1=new Properties(); + props1.setProperty("name", "EventService"); + props1.setProperty("path", "/usr/local/Orbix2000/bin/es"); + props1.setProperty("up", "true"); + props1.setProperty("active", "false"); + + Properties props2=new Properties(); + props2.setProperty("name", "NamingService"); + props2.setProperty("path", "/usr/local/Orbix2000/bin/ns"); + props2.setProperty("up", "true"); + props2.setProperty("active", "true"); + + Properties props3=new Properties(); + props3.setProperty("name", "ORBIX daemon"); + props3.setProperty("path", "/usr/local/Orbix2000/bin/orbixd"); + props3.setProperty("up", "true"); + props3.setProperty("active", "true"); + props3.setProperty("restart", "true"); + props3.setProperty("restart_time", "3000"); + props3.setProperty("restart_max", "10"); + + Properties props4=new Properties(); + props4.setProperty("name", "Orbix2000 Version 1.1"); + props4.setProperty("valid until", "11/12/2001"); + props4.setProperty("up", "false"); + props4.setProperty("active", "false"); + + Properties props5=new Properties(); + props5.setProperty("name", "Orbix2000 Version 1.3b"); + props5.setProperty("valid until", "12/31/2000"); + props5.setProperty("up", "true"); + props5.setProperty("active", "false"); + + + if(create) { + dt.add("/procs/NETSMART/es", props1); + dt.add("/procs/NETSMART/ns", props2); + dt.add("/procs/NETSMART/orbixd", props3); + dt.add("/procs/NETSMART/orbixd/Version_1.1", props4); + dt.add("/procs/NETSMART/orbixd/Version_1.2", props5); + Properties props6=(Properties)props5.clone(); + props6.setProperty("name", "osagent daemon"); + props6.setProperty("path", "/usr/local/Visigenics/bin/osagent"); + + Properties props7=new Properties(); + props7.setProperty("name", "Visigenics latest product"); + props7.setProperty("license", "/vob/iem/Devp/etc/license.txt"); + + dt.set("/procs/NETSMART/orbixd/Version_1.2", props6); + dt.add("/procs/NETSMART/orbixd/Version_2.0", props7); + } + + + jtree.addTreeSelectionListener(this); + + + MouseListener ml=new MouseAdapter() { + public void mouseClicked(MouseEvent e) { + int selRow=jtree.getRowForLocation(e.getX(), e.getY()); + TreePath selPath=jtree.getPathForLocation(e.getX(), e.getY()); + if(selRow != -1) + selected_node=makeFQN(selPath.getPath()); + } + }; + jtree.addMouseListener(ml); + + } + + + String makeFQN(Object[] path) { + StringBuilder sb=new StringBuilder(""); + String tmp_name; + + if(path == null) return null; + for(int i=0; i < path.length; i++) { + tmp_name=((MyNode)path[i]).name; + if("/".equals(tmp_name)) + continue; + else + sb.append('/' + tmp_name); + } + tmp_name=sb.toString(); + if(tmp_name.length() == 0) + return "/"; + else + return tmp_name; + } + + + void clearTable() { + int num_rows=table.getRowCount(); + + if(num_rows > 0) { + for(int i=0; i < num_rows; i++) + table_model.removeRow(0); + table_model.fireTableRowsDeleted(0, num_rows - 1); + repaint(); + } + } + + void populateTable(Properties props) { + String key, val; + int num_rows=0; + + if(props == null) return; + num_rows=props.size(); + clearTable(); + + if(num_rows > 0) { + for(Enumeration e=props.keys(); e.hasMoreElements();) { + key=(String)e.nextElement(); + val=(String)props.get(key); + if(val == null) val=""; + table_model.addRow(new Object[]{key, val}); + } + + table_model.fireTableRowsInserted(0, num_rows - 1); + validate(); + } + + } + + + void populateTree(DistributedTree tree, String tmp_fqn) { + if(tree == null) return; + Vector children=tree.getChildrenNames(tmp_fqn); + String child_name, tmp_name; + Serializable element; + + for(int i=0; i < children.size(); i++) { + child_name=(String)children.elementAt(i); + tmp_name=tmp_fqn + '/' + child_name; + root.add(tmp_name, tree.get(tmp_name)); + populateTree(tree, tmp_name); + } + } + + + public synchronized void setVisible(boolean show) { + setLocation(50, 50); + super.setVisible(show); + } + + public void windowClosed(WindowEvent event) { + } + + public void windowDeiconified(WindowEvent event) { + } + + public void windowIconified(WindowEvent event) { + } + + public void windowActivated(WindowEvent event) { + } + + public void windowDeactivated(WindowEvent event) { + } + + public void windowOpened(WindowEvent event) { + } + + public void windowClosing(WindowEvent event) { + dt.stop(); + System.exit(0); + } + + + public void tableChanged(TableModelEvent evt) { + int row, col; + String key, val; + + if(evt.getType() == TableModelEvent.UPDATE) { + row=evt.getFirstRow(); + col=evt.getColumn(); + + Properties props=(Properties)dt.get(selected_node); + if(col == 0) { // set() + key=(String)table_model.getValueAt(row, col); + val=(String)table_model.getValueAt(row, col + 1); + if(props != null && key != null && val != null) { + props.setProperty(key, val); + dt.set(selected_node, props); + } + } + else { // add() + key=(String)table_model.getValueAt(row, col - 1); + val=(String)table.getValueAt(row, col); + if(props != null && key != null && val != null) { + props.setProperty(key, val); + dt.add(selected_node, props); + } + } + System.out.println("key=" + key + ", val=" + val); + + } + } + + + public void valueChanged(TreeSelectionEvent evt) { + TreePath path=evt.getPath(); + String fqn="/"; + String component_name; + Properties props=null; + + for(int i=0; i < path.getPathCount(); i++) { + component_name=((MyNode)path.getPathComponent(i)).name; + if("/".equals(component_name)) + continue; + if("/".equals(fqn)) + fqn+=component_name; + else + fqn=fqn + '/' + component_name; + } + props=(Properties)dt.get(fqn); + if(props != null) + populateTable(props); + else + clearTable(); + } + + /* ------------------ DistributedTree.DistributedTreeListener interface ------------ */ + + public void nodeAdded(String fqn, Serializable element) { + MyNode n; + System.out.println("** nodeCreated(" + fqn + ')'); + + root.add(fqn, element); + n=root.findNode(fqn); + if(n != null) + tree_model.reload(n.getParent()); + } + + public void nodeRemoved(String fqn) { + MyNode n; + TreeNode par; + System.out.println("** nodeRemoved(" + fqn + ')'); + n=root.findNode(fqn); + if(n != null) { + n.removeAllChildren(); + par=n.getParent(); + n.removeFromParent(); + tree_model.reload(par); + } + } + + public void nodeModified(String fqn, Serializable old_element, Serializable new_element) { + System.out.println("** nodeModified(" + fqn + ')'); + root.modify(fqn, new_element); + populateTable((Properties)new_element); + } + + /* ---------------- End of DistributedTree.DistributedTreeListener interface -------- */ + + + public static void main(String args[]) { + DistributedTreeDemo demo; + boolean create=false; + + for(int i=0; i < args.length; i++) { + if("-help".equals(args[i])) { + System.out.println("DistributedTreeDemo [-create] [-help]"); + return; + } + if("-create".equals(args[i])) { + create=true; + continue; + } + } + + try { + demo=new DistributedTreeDemo(create); + demo.setVisible(true); + } + catch(Exception ex) { + System.err.println(ex); + } + } +} Index: 3rdParty_sources/jgroups/org/jgroups/demos/Draw.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/demos/Draw.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/demos/Draw.java 17 Aug 2012 14:51:23 -0000 1.1 @@ -0,0 +1,617 @@ +// $Id: Draw.java,v 1.1 2012/08/17 14:51:23 marcin Exp $ + + +package org.jgroups.demos; + + +import org.jgroups.*; +import org.jgroups.jmx.JmxConfigurator; +import org.jgroups.util.Util; + +import javax.management.MBeanServer; +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.util.*; +import java.io.*; + + +/** + * Shared whiteboard, each new instance joins the same group. Each instance chooses a random color, + * mouse moves are broadcast to all group members, which then apply them to their canvas

+ * @author Bela Ban, Oct 17 2001 + */ +public class Draw extends ExtendedReceiverAdapter implements ActionListener, ChannelListener { + String groupname="DrawGroupDemo"; + private Channel channel=null; + private int member_size=1; + static final boolean first=true; + private JFrame mainFrame=null; + private JPanel sub_panel=null; + private DrawPanel panel=null; + private JButton clear_button, leave_button; + private final Random random=new Random(System.currentTimeMillis()); + private final Font default_font=new Font("Helvetica",Font.PLAIN,12); + private final Color draw_color=selectColor(); + private static final Color background_color=Color.white; + boolean no_channel=false; + boolean jmx; + private boolean use_state=false; + private long state_timeout=5000; + + + public Draw(String props, boolean no_channel, boolean jmx, boolean use_state, long state_timeout, + boolean use_blocking) throws Exception { + this.no_channel=no_channel; + this.jmx=jmx; + this.use_state=use_state; + this.state_timeout=state_timeout; + if(no_channel) + return; + + channel=new JChannel(props); + if(use_blocking) + channel.setOpt(Channel.BLOCK, Boolean.TRUE); + channel.setOpt(Channel.AUTO_RECONNECT, Boolean.TRUE); + channel.setReceiver(this); + channel.addChannelListener(this); + } + + public Draw(Channel channel) throws Exception { + this.channel=channel; + channel.setOpt(Channel.AUTO_RECONNECT, Boolean.TRUE); + channel.setReceiver(this); + channel.addChannelListener(this); + } + + + public Draw(Channel channel, boolean use_state, long state_timeout) throws Exception { + this.channel=channel; + channel.setOpt(Channel.AUTO_RECONNECT, Boolean.TRUE); + channel.setReceiver(this); + channel.addChannelListener(this); + this.use_state=use_state; + this.state_timeout=state_timeout; + } + + + public String getGroupName() { + return groupname; + } + + public void setGroupName(String groupname) { + if(groupname != null) + this.groupname=groupname; + } + + + public static void main(String[] args) { + Draw draw=null; + String props=null; + boolean no_channel=false; + boolean jmx=false; + boolean use_state=false; + boolean use_blocking=false; + String group_name=null; + long state_timeout=5000; + + for(int i=0; i < args.length; i++) { + if("-help".equals(args[i])) { + help(); + return; + } + if("-props".equals(args[i])) { + props=args[++i]; + continue; + } + if("-no_channel".equals(args[i])) { + no_channel=true; + continue; + } + if("-jmx".equals(args[i])) { + jmx=true; + continue; + } + if("-groupname".equals(args[i])) { + group_name=args[++i]; + continue; + } + if("-state".equals(args[i])) { + use_state=true; + continue; + } + if("-use_blocking".equals(args[i])) { + use_blocking=true; + continue; + } + if("-timeout".equals(args[i])) { + state_timeout=Long.parseLong(args[++i]); + continue; + } + if("-bind_addr".equals(args[i])) { + System.setProperty("jgroups.bind_addr", args[++i]); + continue; + } + + help(); + return; + } + + try { + draw=new Draw(props, no_channel, jmx, use_state, state_timeout, use_blocking); + if(group_name != null) + draw.setGroupName(group_name); + draw.go(); + } + catch(Throwable e) { + e.printStackTrace(); + System.exit(0); + } + } + + + static void help() { + System.out.println("\nDraw [-help] [-no_channel] [-props ]" + + " [-groupname ] [-state] [-use_blocking] [-timeout ] [-bind_addr ]"); + System.out.println("-no_channel: doesn't use JGroups at all, any drawing will be relected on the " + + "whiteboard directly"); + System.out.println("-props: argument can be an old-style protocol stack specification, or it can be " + + "a URL. In the latter case, the protocol specification will be read from the URL\n"); + } + + + private Color selectColor() { + int red=(Math.abs(random.nextInt()) % 255); + int green=(Math.abs(random.nextInt()) % 255); + int blue=(Math.abs(random.nextInt()) % 255); + return new Color(red, green, blue); + } + + + + public void go() throws Exception { + if(!no_channel && !use_state) { + channel.connect(groupname); + if(jmx) { + MBeanServer server=Util.getMBeanServer(); + if(server == null) + throw new Exception("No MBeanServers found;" + + "\nDraw needs to be run with an MBeanServer present, or inside JDK 5"); + JmxConfigurator.registerChannel((JChannel)channel, server, "jgroups", channel.getClusterName(), true); + } + + } + mainFrame=new JFrame(); + mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + panel=new DrawPanel(use_state); + panel.setBackground(background_color); + sub_panel=new JPanel(); + mainFrame.getContentPane().add("Center", panel); + clear_button=new JButton("Clear"); + clear_button.setFont(default_font); + clear_button.addActionListener(this); + leave_button=new JButton("Leave"); + leave_button.setFont(default_font); + leave_button.addActionListener(this); + sub_panel.add("South", clear_button); + sub_panel.add("South", leave_button); + mainFrame.getContentPane().add("South", sub_panel); + mainFrame.setBackground(background_color); + clear_button.setForeground(Color.blue); + leave_button.setForeground(Color.blue); + mainFrame.pack(); + mainFrame.setLocation(15, 25); + mainFrame.setBounds(new Rectangle(250, 250)); + + if(!no_channel && use_state) { + channel.connect(groupname,null,null, state_timeout); + } + mainFrame.setVisible(true); + setTitle(); + } + + + + + void setTitle(String title) { + String tmp=""; + if(no_channel) { + mainFrame.setTitle(" Draw Demo "); + return; + } + if(title != null) { + mainFrame.setTitle(title); + } + else { + if(channel.getLocalAddress() != null) + tmp+=channel.getLocalAddress(); + tmp+=" (" + member_size + ")"; + mainFrame.setTitle(tmp); + } + } + + void setTitle() { + setTitle(null); + } + + + + public void receive(Message msg) { + byte[] buf=msg.getRawBuffer(); + if(buf == null) { + System.err.println("[" + channel.getLocalAddress() + "] received null buffer from " + msg.getSrc() + + ", headers: " + msg.printHeaders()); + return; + } + + try { + DrawCommand comm=(DrawCommand)Util.streamableFromByteBuffer(DrawCommand.class, buf, msg.getOffset(), msg.getLength()); + switch(comm.mode) { + case DrawCommand.DRAW: + if(panel != null) + panel.drawPoint(comm); + break; + case DrawCommand.CLEAR: + clearPanel(); + break; + default: + System.err.println("***** received invalid draw command " + comm.mode); + break; + } + } + catch(Exception e) { + e.printStackTrace(); + } + } + + public void viewAccepted(View v) { + if(v instanceof MergeView) + System.out.println("** MergeView=" + v); + else + System.out.println("** View=" + v); + member_size=v.size(); + if(mainFrame != null) + setTitle(); + } + + public void block() { + System.out.println("-- received BlockEvent"); + } + + public void unblock() { + System.out.println("-- received UnblockEvent"); + } + + + public byte[] getState() { + return panel.getState(); + } + + public void setState(byte[] state) { + panel.setState(state); + } + + + public void getState(OutputStream ostream) { + try { + try { + panel.writeState(ostream); + } + catch(IOException e) { + e.printStackTrace(); + } + } + finally { + Util.close(ostream); + } + } + + public void setState(InputStream istream) { + try { + try { + panel.readState(istream); + } + catch(IOException e) { + e.printStackTrace(); + } + } + finally { + Util.close(istream); + } + } + + /* --------------- Callbacks --------------- */ + + + + public void clearPanel() { + if(panel != null) + panel.clear(); + } + + public void sendClearPanelMsg() { + DrawCommand comm=new DrawCommand(DrawCommand.CLEAR); + + try { + byte[] buf=Util.streamableToByteBuffer(comm); + channel.send(new Message(null, null, buf)); + } + catch(Exception ex) { + System.err.println(ex); + } + } + + + public void actionPerformed(ActionEvent e) { + String command=e.getActionCommand(); + if("Clear".equals(command)) { + if(no_channel) { + clearPanel(); + return; + } + sendClearPanelMsg(); + } + else if("Leave".equals(command)) { + stop(); + } + else + System.out.println("Unknown action"); + } + + + public void stop() { + if(!no_channel) { + try { + channel.close(); + } + catch(Exception ex) { + System.err.println(ex); + } + } + mainFrame.setVisible(false); + mainFrame.dispose(); + } + + + /* ------------------------------ ChannelListener interface -------------------------- */ + + public void channelConnected(Channel channel) { + + } + + public void channelDisconnected(Channel channel) { + + } + + public void channelClosed(Channel channel) { + + } + + public void channelShunned() { + System.out.println("-- received EXIT, waiting for ChannelReconnected callback"); + setTitle(" Draw Demo - shunned "); + } + + public void channelReconnected(Address addr) { + setTitle(); + } + + + /* --------------------------- End of ChannelListener interface ---------------------- */ + + + + private class DrawPanel extends JPanel implements MouseMotionListener { + final Dimension preferred_size=new Dimension(235, 170); + Image img=null; // for drawing pixels + Dimension d, imgsize=null; + Graphics gr=null; + final Map state; + + + public DrawPanel(boolean use_state) { + if(use_state) + state=new LinkedHashMap(); + else + state=null; + createOffscreenImage(false); + addMouseMotionListener(this); + addComponentListener(new ComponentAdapter() { + public void componentResized(ComponentEvent e) { + if(getWidth() <= 0 || getHeight() <= 0) return; + createOffscreenImage(false); + } + }); + } + + + public byte[] getState() { + byte[] retval=null; + if(state == null) return null; + synchronized(state) { + try { + retval=Util.objectToByteBuffer(state); + } + catch(Exception e) { + e.printStackTrace(); + } + } + return retval; + } + + + public void setState(byte[] buf) { + synchronized(state) { + try { + Map tmp=(Map)Util.objectFromByteBuffer(buf); + state.clear(); + state.putAll(tmp); + System.out.println("received state: " + buf.length + " bytes, " + state.size() + " entries"); + createOffscreenImage(true); + } + catch(Exception e) { + e.printStackTrace(); + } + } + } + + public void writeState(OutputStream outstream) throws IOException { + synchronized(state) { + if(state != null) { + DataOutputStream dos=new DataOutputStream(outstream); + dos.writeInt(state.size()); + Point point; + Color col; + for(Map.Entry entry: state.entrySet()) { + point=entry.getKey(); + col=entry.getValue(); + dos.writeInt(point.x); + dos.writeInt(point.y); + dos.writeInt(col.getRGB()); + } + dos.flush(); + } + } + } + + + public void readState(InputStream instream) throws IOException { + DataInputStream in=new DataInputStream(instream); + Map new_state=new HashMap(); + int num=in.readInt(); + Point point; + Color col; + for(int i=0; i < num; i++) { + point=new Point(in.readInt(), in.readInt()); + col=new Color(in.readInt()); + new_state.put(point, col); + } + + synchronized(state) { + state.clear(); + state.putAll(new_state); + System.out.println("read state: " + state.size() + " entries"); + createOffscreenImage(true); + } + } + + + final void createOffscreenImage(boolean discard_image) { + d=getSize(); + if(discard_image) { + img=null; + imgsize=null; + } + if(img == null || imgsize == null || imgsize.width != d.width || imgsize.height != d.height) { + img=createImage(d.width, d.height); + if(img != null) { + gr=img.getGraphics(); + if(gr != null && state != null) { + drawState(); + } + } + imgsize=d; + } + repaint(); + } + + + /* ---------------------- MouseMotionListener interface------------------------- */ + + public void mouseMoved(MouseEvent e) {} + + public void mouseDragged(MouseEvent e) { + int x=e.getX(), y=e.getY(); + DrawCommand comm=new DrawCommand(DrawCommand.DRAW, x, y, + draw_color.getRed(), draw_color.getGreen(), draw_color.getBlue()); + + if(no_channel) { + drawPoint(comm); + return; + } + + try { + byte[] buf=Util.streamableToByteBuffer(comm); + channel.send(new Message(null, null, buf)); + // Thread.yield(); + } + catch(Exception ex) { + System.err.println(ex); + } + } + + /* ------------------- End of MouseMotionListener interface --------------------- */ + + + /** + * Adds pixel to queue and calls repaint() whenever we have MAX_ITEMS pixels in the queue + * or when MAX_TIME msecs have elapsed (whichever comes first). The advantage compared to just calling + * repaint() after adding a pixel to the queue is that repaint() can most often draw multiple points + * at the same time. + */ + public void drawPoint(DrawCommand c) { + if(c == null || gr == null) return; + Color col=new Color(c.r, c.g, c.b); + gr.setColor(col); + gr.fillOval(c.x, c.y, 10, 10); + repaint(); + if(state != null) { + synchronized(state) { + state.put(new Point(c.x, c.y), col); + } + } + } + + + + public void clear() { + if(gr == null) return; + gr.clearRect(0, 0, getSize().width, getSize().height); + repaint(); + if(state != null) { + synchronized(state) { + state.clear(); + } + } + } + + + + + + /** Draw the entire panel from the state */ + public void drawState() { + // clear(); + Map.Entry entry; + Point pt; + Color col; + synchronized(state) { + for(Iterator it=state.entrySet().iterator(); it.hasNext();) { + entry=(Map.Entry)it.next(); + pt=(Point)entry.getKey(); + col=(Color)entry.getValue(); + gr.setColor(col); + gr.fillOval(pt.x, pt.y, 10, 10); + + } + } + repaint(); + } + + + public Dimension getPreferredSize() { + return preferred_size; + } + + + public void paintComponent(Graphics g) { + super.paintComponent(g); + if(img != null) { + g.drawImage(img, 0, 0, null); + } + } + + } + +} + Index: 3rdParty_sources/jgroups/org/jgroups/demos/Draw2Channels.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/demos/Draw2Channels.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/demos/Draw2Channels.java 17 Aug 2012 14:51:23 -0000 1.1 @@ -0,0 +1,380 @@ +// $Id: Draw2Channels.java,v 1.1 2012/08/17 14:51:23 marcin Exp $ + + +package org.jgroups.demos; + + +import org.jgroups.*; +import org.jgroups.Event; +import org.jgroups.util.Util; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.util.Random; + + +/** + * Same as Draw but using 2 channels: one for view changes (control channel) and the other one for drawing + * (data channel). Ported to use Swing Nov 1 2001, not tested. + * @author Bela Ban, Nov 1 2001 + */ +public class Draw2Channels implements ActionListener { + private final String control_groupname="Draw2ChannelsGroup-Control"; + private final String data_groupname="Draw2ChannelsGroup-Data"; + private Channel control_channel=null; + private Channel data_channel=null; + String control_props=null, data_props=null; + private Receiver control_receiver=null; + private Receiver data_receiver=null; + private int member_size=1; + final boolean first=true; + private JFrame mainFrame=null; + private JPanel sub_panel=null; + private DrawPanel panel=null; + private JButton clear_button, leave_button; + private final Random random=new Random(System.currentTimeMillis()); + private final Font default_font=new Font("Helvetica", Font.PLAIN, 12); + private final Color draw_color=selectColor(); + private final Color background_color=Color.white; + boolean no_channel=false; + + + public Draw2Channels(String control_props, String data_props, boolean no_channel) throws Exception { + + this.control_props=control_props; + this.data_props=data_props; + this.no_channel=no_channel; + } + + + public static void main(String[] args) { + Draw2Channels draw=null; + String control_props=null, data_props=null; + boolean no_channel=false; + + for(int i=0; i < args.length; i++) { + if("-help".equals(args[i])) { + help(); + return; + } + if("-no_channel".equals(args[i])) { + no_channel=true; + continue; + } + help(); + return; + } + + + control_props="UDP(mcast_addr=224.0.0.35;mcast_port=45566;ip_ttl=32;" + + "mcast_send_buf_size=150000;mcast_recv_buf_size=80000):" + + "PING(timeout=2000;num_initial_members=3):" + + "MERGE2(min_interval=5000;max_interval=10000):" + + "FD_SOCK:" + + "VERIFY_SUSPECT(timeout=1500):" + + "pbcast.NAKACK(gc_lag=50;retransmit_timeout=300,600,1200,2400,4800):" + + "UNICAST(timeout=5000):" + + "pbcast.STABLE(desired_avg_gossip=20000):" + + "FRAG(frag_size=4096;down_thread=false;up_thread=false):" + + "pbcast.GMS(join_timeout=5000;" + + "shun=false;print_local_addr=true)"; + + + data_props="UDP(mcast_addr=224.10.10.200;mcast_port=5679)"; + + + try { + + draw=new Draw2Channels(control_props, data_props, no_channel); + draw.go(); + } + catch(Exception e) { + System.err.println(e); + System.exit(0); + } + } + + + static void help() { + System.out.println("Draw2Channels [-help] [-no_channel]"); + } + + + private Color selectColor() { + int red=(Math.abs(random.nextInt()) % 255); + int green=(Math.abs(random.nextInt()) % 255); + int blue=(Math.abs(random.nextInt()) % 255); + return new Color(red, green, blue); + } + + + public void go() { + try { + if(!no_channel) { + control_receiver=new ControlReceiver(); + data_receiver=new DataReceiver(); + System.out.println("Creating control channel"); + control_channel=new JChannel(control_props); + control_channel.setReceiver(control_receiver); + System.out.println("Creating data channel"); + data_channel=new JChannel(data_props); + data_channel.setReceiver(data_receiver); + // data_channel.SetOpt(Channel.VIEW, Boolean.FALSE); + System.out.println("Connecting data channel"); + data_channel.connect(data_groupname); + System.out.println("Connecting control channel"); + control_channel.connect(control_groupname); + } + mainFrame=new JFrame(); + mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + panel=new DrawPanel(); + panel.setBackground(background_color); + sub_panel=new JPanel(); + mainFrame.getContentPane().add("Center", panel); + clear_button=new JButton("Clear"); + clear_button.setFont(default_font); + clear_button.addActionListener(this); + leave_button=new JButton("Leave & Exit"); + leave_button.setFont(default_font); + leave_button.addActionListener(this); + sub_panel.add("South", clear_button); + sub_panel.add("South", leave_button); + mainFrame.getContentPane().add("South", sub_panel); + mainFrame.setVisible(true); + mainFrame.setBackground(background_color); + clear_button.setForeground(Color.blue); + leave_button.setForeground(Color.blue); + setTitle(); + mainFrame.pack(); + mainFrame.setLocation(15, 25); + mainFrame.setVisible(true); + } + catch(Exception e) { + System.err.println(e); + } + } + + + void setTitle() { + String title=""; + if(no_channel) { + mainFrame.setTitle(" Draw Demo "); + return; + } + if(control_channel.getLocalAddress() != null) + title+=control_channel.getLocalAddress(); + title+=" (" + member_size + ") mbrs"; + mainFrame.setTitle(title); + } + + + + public void clearPanel() { + if(panel != null) + panel.clear(); + } + + public void sendClearPanelMsg() { + int tmp[]=new int[1]; + tmp[0]=0; + DrawCommand comm=new DrawCommand(DrawCommand.CLEAR); + + try { + byte[] buf=Util.streamableToByteBuffer(comm); + data_channel.send(new Message(null, null, buf)); + } + catch(Exception ex) { + System.err.println(ex); + } + } + + + public void actionPerformed(ActionEvent e) { + String command=e.getActionCommand(); + if("Clear".equals(command)) { + if(no_channel) { + clearPanel(); + return; + } + sendClearPanelMsg(); + } + else if("Leave & Exit".equals(command)) { + if(!no_channel) { + try { + control_channel.close(); + } + catch(Exception ex) { + System.err.println(ex); + } + try { + data_channel.close(); + } + catch(Exception ex) { + System.err.println(ex); + } + } + mainFrame.setVisible(false); + mainFrame.dispose(); + System.exit(0); + } + else + System.out.println("Unknown action"); + } + + + private class DrawPanel extends JPanel implements MouseMotionListener { + final Dimension preferred_size=new Dimension(235, 170); + Image img=null; // for drawing pixels + Dimension d, imgsize; + Graphics gr=null; + + + public DrawPanel() { + addMouseMotionListener(this); + addComponentListener(new ComponentAdapter() { + public void componentResized(ComponentEvent e) { + if(getWidth() <= 0 || getHeight() <= 0) return; + createOffscreenImage(); + } + }); + } + + + void createOffscreenImage() { + d=getSize(); + if(img == null || imgsize == null || imgsize.width != d.width || imgsize.height != d.height) { + img=createImage(d.width, d.height); + gr=img.getGraphics(); + imgsize=d; + } + } + + /* ---------------------- MouseMotionListener interface------------------------- */ + + public void mouseMoved(MouseEvent e) { + } + + public void mouseDragged(MouseEvent e) { + int x=e.getX(), y=e.getY(); + DrawCommand comm=new DrawCommand(DrawCommand.DRAW, x, y, + draw_color.getRed(), draw_color.getGreen(), draw_color.getBlue()); + + if(no_channel) { + drawPoint(comm); + return; + } + + try { + byte[] buf=Util.streamableToByteBuffer(comm); + data_channel.send(new Message(null, null, buf)); + Thread.yield(); // gives the repainter some breath + } + catch(Exception ex) { + System.err.println(ex); + } + } + + /* ------------------- End of MouseMotionListener interface --------------------- */ + + + /** + * Adds pixel to queue and calls repaint() whenever we have MAX_ITEMS pixels in the queue + * or when MAX_TIME msecs have elapsed (whichever comes first). The advantage compared to just calling + * repaint() after adding a pixel to the queue is that repaint() can most often draw multiple points + * at the same time. + */ + public void drawPoint(DrawCommand c) { + if(c == null) return; + gr.setColor(new Color(c.r, c.g, c.b)); + gr.fillOval(c.x, c.y, 10, 10); + repaint(); + } + + + public void clear() { + gr.clearRect(0, 0, getSize().width, getSize().height); + repaint(); + } + + + public Dimension getPreferredSize() { + return preferred_size; + } + + + public void paintComponent(Graphics g) { + super.paintComponent(g); + if(img != null) { + g.drawImage(img, 0, 0, null); + } + } + + } + + + class ControlReceiver extends ExtendedReceiverAdapter { + public void viewAccepted(View v) { + member_size=v.size(); + if(mainFrame != null) + mainFrame.setTitle(member_size + " mbrs"); + data_channel.down(new Event(Event.VIEW_CHANGE, v)); + } + } + + + class DataReceiver extends ExtendedReceiverAdapter implements ChannelListener { + + public void receive(Message msg) { + byte[] buf=msg.getRawBuffer(); + DrawCommand comm=null; + try { + comm=(DrawCommand)Util.streamableFromByteBuffer(DrawCommand.class, buf, msg.getOffset(), msg.getLength()); + switch(comm.mode) { + case DrawCommand.DRAW: + if(panel != null) + panel.drawPoint(comm); + break; + case DrawCommand.CLEAR: + clearPanel(); + break; + default: + System.err.println("***** Draw2Channels.run(): received invalid draw command " + comm.mode); + break; + } + } + catch(Exception ex) { + ex.printStackTrace(); + } + } + + public void viewAccepted(View v) { + System.out.println("** View=" + v); + member_size=v.size(); + if(mainFrame != null) + setTitle(); + } + + + public void channelConnected(Channel channel) { + } + + public void channelDisconnected(Channel channel) { + } + + public void channelClosed(Channel channel) { + } + + public void channelShunned() { + System.out.println("received EXIT, waiting for ChannelReconnected callback"); + } + + public void channelReconnected(Address addr) { + } + } + + +} + + + Index: 3rdParty_sources/jgroups/org/jgroups/demos/DrawCommand.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/demos/DrawCommand.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/demos/DrawCommand.java 17 Aug 2012 14:51:23 -0000 1.1 @@ -0,0 +1,75 @@ +// $Id: DrawCommand.java,v 1.1 2012/08/17 14:51:23 marcin Exp $ + +package org.jgroups.demos; + +import org.jgroups.util.Streamable; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.DataInputStream; + +/** + * Encapsulates information about a draw command. + * Used by the {@link Draw} and other demos. + * + */ +public class DrawCommand implements Streamable { + static final byte DRAW=1; + static final byte CLEAR=2; + byte mode; + int x=0; + int y=0; + int r=0; + int g=0; + int b=0; + + public DrawCommand() { // needed for streamable + } + + DrawCommand(byte mode) { + this.mode=mode; + } + + DrawCommand(byte mode, int x, int y, int r, int g, int b) { + this.mode=mode; + this.x=x; + this.y=y; + this.r=r; + this.g=g; + this.b=b; + } + + + public void writeTo(DataOutputStream out) throws IOException { + out.writeByte(mode); + out.writeInt(x); + out.writeInt(y); + out.writeInt(r); + out.writeInt(g); + out.writeInt(b); + } + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + mode=in.readByte(); + x=in.readInt(); + y=in.readInt(); + r=in.readInt(); + g=in.readInt(); + b=in.readInt(); + } + + + public String toString() { + StringBuilder ret=new StringBuilder(); + switch(mode) { + case DRAW: ret.append("DRAW(" + x + ", " + y + ") [" + r + '|' + g + '|' + b + ']'); + break; + case CLEAR: ret.append("CLEAR"); + break; + default: + return ""; + } + return ret.toString(); + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/demos/DrawMultiplexer.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/demos/DrawMultiplexer.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/demos/DrawMultiplexer.java 17 Aug 2012 14:51:23 -0000 1.1 @@ -0,0 +1,52 @@ +package org.jgroups.demos; + +import org.jgroups.Channel; +import org.jgroups.JChannelFactory; + +/** + * @author Bela Ban + * @version $Id: DrawMultiplexer.java,v 1.1 2012/08/17 14:51:23 marcin Exp $ + */ +public class DrawMultiplexer { + JChannelFactory factory; + + public static void main(String[] args) throws Exception { + String props="stacks.xml"; + String stack_name="udp"; + boolean state=false; + for(int i=0; i < args.length; i++) { + String arg=args[i]; + if(arg.equals("-props")) { + props=args[++i]; + continue; + } + if(arg.equals("-stack_name")) { + stack_name=args[++i]; + continue; + } + if(arg.equals("-state")) { + state=true; + continue; + } + System.out.println("DrawMultiplexer [-help] [-props ] [-stack_name ] [-state]"); + return; + } + new DrawMultiplexer().start(props, stack_name, state); + } + + + private void start(String props, String stack_name, boolean state) throws Exception { + factory=new JChannelFactory(); + factory.setMultiplexerConfig(props); + + final Channel ch1, ch2; + ch1=factory.createMultiplexerChannel(stack_name, "id-1"); + Draw draw1=new Draw(ch1, state, 5000); + + ch2=factory.createMultiplexerChannel(stack_name, "id-2"); + Draw draw2=new Draw(ch2, state, 5000); + + draw1.go(); + draw2.go(); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/demos/DrawRepl.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/demos/DrawRepl.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/demos/DrawRepl.java 17 Aug 2012 14:51:24 -0000 1.1 @@ -0,0 +1,346 @@ +// $Id: DrawRepl.java,v 1.1 2012/08/17 14:51:24 marcin Exp $ + +package org.jgroups.demos; + + +import org.jgroups.Channel; +import org.jgroups.JChannel; +import org.jgroups.blocks.GroupRequest; +import org.jgroups.blocks.MethodCall; +import org.jgroups.blocks.RpcDispatcher; + +import java.awt.*; +import java.awt.event.*; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.util.Hashtable; +import java.util.Random; + + + + +/** + * + * Replicates the whiteboard demo by intercepting central AWT event queue and mcasting events to + * all members. Not very useful in all cases, e.g. when the "Leave" button is pressed, and this event + * is broadcast to all members, all members will leave ! This demo would clearly benefit from more work ! + * NOT SUPPORTED ! + */ +public class DrawRepl implements MouseMotionListener, WindowListener, ActionListener, + Runnable { + private Graphics graphics=null; + private Frame mainFrame=null; + private Panel panel=null, sub_panel=null; + private final byte[] buf=new byte[128]; + private final ByteArrayOutputStream out=new ByteArrayOutputStream(); + private DataOutputStream outstream; + private ByteArrayInputStream inp; + private DataInputStream instream; + private int x, y; + private final Hashtable colors=new Hashtable(); + private final Random random=new Random(System.currentTimeMillis()); + private int col_val=1; + private Color current_color=Color.red; + private Button clear_button, leave_button; + private final String groupname="DrawReplGroup"; + private final Font default_font=new Font("Helvetica",Font.PLAIN,12); + + private EventQueue event_queue=null; + private Thread mythread=null; + private RpcDispatcher dispatcher; + private Channel channel; + + + public DrawRepl() { + colors.put(new Integer(1), Color.white); + colors.put(new Integer(2), Color.black); + colors.put(new Integer(3), Color.red); + colors.put(new Integer(4), Color.orange); + colors.put(new Integer(5), Color.green); + colors.put(new Integer(6), Color.magenta); + colors.put(new Integer(7), Color.cyan); + colors.put(new Integer(8), Color.blue); + mythread=new Thread(this); + try { + channel=new JChannel(); + dispatcher=new RpcDispatcher(channel, null, null, this); + channel.connect(groupname); + } + catch(Exception e) { + System.err.println(e); + System.exit(0); + } + } + + + public static void main(String[] args) { + DrawRepl draw=new DrawRepl(); + draw.go(); + } + + + private Color SelectColor() { + col_val=(Math.abs(random.nextInt()) % 8)+1; + Color ret=(Color)colors.get(new Integer(col_val)); + if(ret == null) + ret=Color.red; + return ret; + } + + + + AWTEvent copyEvent(Component src, AWTEvent evt) { + + if(evt instanceof MouseEvent) { + MouseEvent mev=(MouseEvent)evt; + return new MouseEvent(src, evt.getID(), mev.getWhen(), mev.getModifiers(), + mev.getX(), mev.getY(), mev.getClickCount(), + mev.isPopupTrigger()); + } + + if(evt instanceof KeyEvent) { + KeyEvent kev=(KeyEvent)evt; + return new KeyEvent(src, evt.getID(), kev.getWhen(), kev.getModifiers(), + kev.getKeyCode(), kev.getKeyChar()); + } + + if(evt instanceof ActionEvent) + return new ActionEvent(src, evt.getID(), ((ActionEvent)evt).getActionCommand(), + ((ActionEvent)evt).getModifiers()); + + + if(evt instanceof PaintEvent) + return new PaintEvent(src, evt.getID(), ((PaintEvent)evt).getUpdateRect()); + + + if(evt instanceof FocusEvent) + return new FocusEvent(src, evt.getID(), ((FocusEvent)evt).isTemporary()); + + if(evt instanceof ComponentEvent) + return new ComponentEvent(src, evt.getID()); + + return null; + } + + + + void dispatch(Object src, AWTEvent evt) { + if (src instanceof Component) + ((Component)src).dispatchEvent(evt); + else if (src instanceof MenuComponent) + ((MenuComponent)src).dispatchEvent(evt); + else + System.err.println("++++++++++"); + } + + + public Component findComponent(Container parent, String comp_name) { + Component retval=null; + + if(comp_name != null && comp_name.equals(parent.getName())) + return parent; + + int ncomponents = parent.getComponentCount(); + Component components[] = parent.getComponents(); + for (int i = ncomponents-1 ; i >= 0; i--) { + Component comp = components[i], tmp; + if (comp != null) { + if(comp instanceof Container) { + retval=findComponent((Container)comp, comp_name); + if(retval != null) + return retval; + } + else if(comp_name.equals(comp.getName())) + return comp; + } + } + return retval; + } + + + // public void setSize(Integer x, Integer y) { + // mainFrame.setSize(new Dimension(x.intValue(), y.intValue())); + // } + + + /* Called by Dispatcher */ + public void processEvent(String comp_name, AWTEvent evt) { + AWTEvent copy_evt=null; + Component src=findComponent(mainFrame, comp_name); + if(src == null) { + System.err.println("processEvent(): src is null"); + return; + } + + System.out.println("Received " + evt.getClass().getName()); + + copy_evt=copyEvent(src, evt); + if(copy_evt == null) { + System.err.println("copy_evt is NULL"); + return; + } + dispatch(src, copy_evt); + + +// if(evt instanceof ComponentEvent && evt.getID() == ComponentEvent.COMPONENT_RESIZED) { +// Dimension dim=mainFrame.getSize(); +// try { +// dispatcher.sendGetN(groupname, "setSize", new Integer(dim.height), +// new Integer(dim.width), 0, 0); +// } +// catch(Exception e) { +// System.err.println(e); +// } +// } + } + + + void processLocally(AWTEvent evt) { + dispatch(evt.getSource(), evt); + } + + + + public void run() { + String comp_name; + + while(true) { + try { + AWTEvent evt=event_queue.getNextEvent(); + Object obj=evt.getSource(); + if(obj == null) { + System.err.println("src is NULL"); + continue; + } + + if(obj instanceof Component) + comp_name=((Component)obj).getName(); + else if(obj instanceof MenuComponent) + comp_name=((MenuComponent)obj).getName(); + else { + System.err.println("src is of type " + obj.getClass().getName()); + continue; + } + + if(evt instanceof FocusEvent || evt instanceof PaintEvent) { + System.out.println(evt.getClass().getName() + " not copied"); + processLocally(evt); + continue; + } + System.out.println("MCasting "+evt.getClass().getName()+" event..."); + MethodCall call = new MethodCall("processEvent", new Object[] {comp_name, evt}, + new String[] {String.class.getName(), AWTEvent.class.getName()}); + dispatcher.callRemoteMethods(null, call, GroupRequest.GET_NONE, 0); + } + catch(Exception e) { + System.err.println(e); + } + } + } + + + + + public void go() { + mainFrame=new Frame(); + panel=new Panel(); + sub_panel=new Panel(); + + event_queue=mainFrame.getToolkit().getSystemEventQueue(); + mythread.start(); + + mainFrame.setSize(200,200); + mainFrame.add("Center", panel); + clear_button=new Button("Clear"); + clear_button.setFont(default_font); + clear_button.addActionListener(this); + leave_button=new Button("Exit"); + leave_button.setFont(default_font); + leave_button.addActionListener(this); + sub_panel.add("South", clear_button); + sub_panel.add("South", leave_button); + mainFrame.add("South", sub_panel); + + mainFrame.addWindowListener(this); + // mainFrame.addComponentListener(this); + + panel.addMouseMotionListener(this); + + mainFrame.setVisible(true); + + graphics=panel.getGraphics(); + current_color=SelectColor(); + if(current_color == null) + current_color=Color.red; + graphics.setColor(current_color); + } + + + + /* --------------- Callbacks --------------- */ + + + public void mouseMoved(MouseEvent e) { + } + + public void mouseDragged(MouseEvent e) { + x=e.getX(); + y=e.getY(); + graphics.fillOval(x, y, 10, 10); + } + + + public void clearPanel() { + + System.out.println("CLEAR"); + + Rectangle bounds=panel.getBounds(); + graphics.clearRect(0, 0, bounds.width, bounds.height); + } + + + + public void windowActivated(WindowEvent e) {} + public void windowClosed(WindowEvent e) {} + public void windowClosing(WindowEvent e) { + System.exit(0); + } + public void windowDeactivated(WindowEvent e) {} + public void windowDeiconified(WindowEvent e) {} + public void windowIconified(WindowEvent e) {} + public void windowOpened(WindowEvent e) {} + + + +// public void componentResized(ComponentEvent e) { +// System.out.println("RESIZED, size is " + mainFrame.getBounds()); +// } + +// public void componentMoved(ComponentEvent e) { +// System.out.println("MOVED, location is: " + mainFrame.getLocation()); +// } + +// public void componentShown(ComponentEvent e) {} + +// public void componentHidden(ComponentEvent e) {} + + + + + public void actionPerformed(ActionEvent e) { + String command=e.getActionCommand(); + if("Clear".equals(command)) + clearPanel(); + else if("Exit".equals(command)) { + mainFrame.setVisible(false); + System.exit(0); + } + else + System.out.println("Unknown action"); + } + + +} + Index: 3rdParty_sources/jgroups/org/jgroups/demos/Gossip.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/demos/Gossip.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/demos/Gossip.java 17 Aug 2012 14:51:23 -0000 1.1 @@ -0,0 +1,527 @@ +// $Id: Gossip.java,v 1.1 2012/08/17 14:51:23 marcin Exp $ + +package org.jgroups.demos; + + +import org.jgroups.*; +import org.jgroups.util.Util; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.io.ByteArrayOutputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.Random; +import java.util.Vector; + + + + +/** + * Demos that tries to graphically illustrating the gossip (or pbcast) protocol: every sender periodically sends + * a DRAW command to a random subset of the group members. Each member checks whether it already received the + * message and applies it if not yet received. Otherwise it discards it. If not yet received, the message will + * be forwarded to 10% of the group members. This demo is probably only interesting when we have a larger + * number of members: a gossip will gradually reach all members, coloring their whiteboards. + */ +public class Gossip implements Runnable, WindowListener, ActionListener, ChannelListener { + private Graphics graphics=null; + private Frame mainFrame=null; + private JPanel panel=null, sub_panel=null; + private final ByteArrayOutputStream out=new ByteArrayOutputStream(); + private final Random random=new Random(System.currentTimeMillis()); + private Button gossip_button, clear_button, leave_button; + private final Font default_font=new Font("Helvetica", Font.PLAIN, 12); + private final String groupname="GossipGroupDemo"; + private Channel channel=null; + private Thread receiver=null; + private int member_size=1; + private final Vector members=new Vector(); + private int red=0, green=0, blue=0; + private Color default_color=null; + boolean first=true; + final double subset=0.1; + Address local_addr=null; + TrafficGenerator gen=null; + long traffic_interval=0; + + + public Gossip(String props, long traffic) throws Exception { + + channel=new JChannel(props); + channel.addChannelListener(this); + channel.setOpt(Channel.AUTO_RECONNECT, Boolean.TRUE); + traffic_interval=traffic; + if(traffic_interval > 0) { + gen=new TrafficGenerator(); + gen.start(); + } + } + + + public static void main(String[] args) { + Gossip gossip=null; + String props=null; + long traffic=0; + + + for(int i=0; i < args.length; i++) { + if("-help".equals(args[i])) { + System.out.println("Gossip [-traffic_interval ] [-help]"); + return; + } + if("-traffic_interval".equals(args[i])) { + traffic=Long.parseLong(args[++i]); + continue; + } + } + + + // props="UDP:PING:FD:STABLE:NAKACK:UNICAST:FRAG:FLUSH:GMS:VIEW_ENFORCER:PERF(trace=;details=true)"; + + + + /** + props="TCP(start_port=8000):" + + "TCPPING(num_initial_members=1;timeout=3000;port_range=2;"+ + "initial_hosts=daddy[8000],terrapin[8000],sindhu[8000]):" + + "FD:" + + "pbcast.PBCAST(gossip_interval=5000;gc_lag=50):" + + "UNICAST:" + + "FRAG:" + + "pbcast.GMS"; + // "PERF(trace=true;details=true)"; + **/ + + + + + + props="UDP(mcast_addr=224.10.10.100;mcast_port=5678;ip_ttl=32):" + + "PING:" + + // "FD(shun=true;timeout=5000):" + + "pbcast.FD(timeout=3000):" + + "VERIFY_SUSPECT(timeout=2000;num_msgs=2):" + + "pbcast.PBCAST(desired_avg_gossip=8000;mcast_gossip=true;gc_lag=30;max_queue=20):" + + "UNICAST:" + + "FRAG:" + + "pbcast.GMS"; // :" + // ;join_timeout=20):" + + // "PERF(trace=true;details=true)"; + + + + try { + gossip=new Gossip(props, traffic); + gossip.go(); + } + catch(Exception e) { + System.err.println(e); + System.exit(0); + } + } + + + private void selectColor() { + red=(Math.abs(random.nextInt()) % 255); + green=(Math.abs(random.nextInt()) % 255); + blue=(Math.abs(random.nextInt()) % 255); + default_color=new Color(red, green, blue); + } + + + public void go() { + try { + channel.connect(groupname); + local_addr=channel.getLocalAddress(); + startThread(); + mainFrame=new Frame(); + panel=new MyPanel(); + sub_panel=new JPanel(); + mainFrame.setSize(250, 250); + mainFrame.add("Center", panel); + clear_button=new Button("Clear"); + clear_button.setFont(default_font); + clear_button.addActionListener(this); + gossip_button=new Button("Gossip"); + gossip_button.setFont(default_font); + gossip_button.addActionListener(this); + leave_button=new Button("Leave & Exit"); + leave_button.setFont(default_font); + leave_button.addActionListener(this); + sub_panel.add("South", gossip_button); + sub_panel.add("South", clear_button); + sub_panel.add("South", leave_button); + mainFrame.add("South", sub_panel); + mainFrame.addWindowListener(this); + mainFrame.setVisible(true); + setTitle(); + graphics=panel.getGraphics(); + graphics.setColor(default_color); + mainFrame.setBackground(Color.white); + mainFrame.pack(); + gossip_button.setForeground(Color.blue); + clear_button.setForeground(Color.blue); + leave_button.setForeground(Color.blue); + } + catch(Exception e) { + System.err.println(e); + return; + } + } + + + void startThread() { + receiver=new Thread(this, "GossipThread"); + receiver.setPriority(Thread.MAX_PRIORITY); + receiver.start(); + } + + + void setTitle() { + String title=""; + if(local_addr != null) + title+=local_addr; + title+=" (" + member_size + ") mbrs"; + mainFrame.setTitle(title); + } + + + public void run() { + Object tmp; + Message msg=null; + Command comm; + boolean fl=true; + Vector mbrs; + ObjectOutputStream os; + + while(fl) { + try { + tmp=channel.receive(0); + // System.out.println("Gossip.run(): received " + tmp); + + if(tmp == null) continue; + + if(tmp instanceof View) { + View v=(View)tmp; + member_size=v.size(); + mbrs=v.getMembers(); + members.removeAllElements(); + for(int i=0; i < mbrs.size(); i++) + members.addElement(mbrs.elementAt(i)); + if(mainFrame != null) + setTitle(); + continue; + } + + if(tmp instanceof ExitEvent) { + // System.out.println("-- Gossip.main(): received EXIT, waiting for ChannelReconnected callback"); + break; + } + + if(!(tmp instanceof Message)) + continue; + + msg=(Message)tmp; + comm=null; + + Object obj=msg.getObject(); + + // System.out.println("obj is " + obj); + + if(obj instanceof Command) + comm=(Command)obj; + else + if(obj instanceof Message) { + System.out.println("*** Message is " + Util.printMessage((Message)obj)); + Util.dumpStack(true); + } + else { + if(obj != null) + System.out.println("obj is " + obj.getClass() + ", hdrs are" + msg.printObjectHeaders()); + else + System.out.println("hdrs are" + msg.printObjectHeaders()); + Util.dumpStack(true); + } + + switch(comm.mode) { + case Command.GOSSIP: + if(graphics != null) { + colorPanel(comm.r, comm.g, comm.b); + comm.not_seen.removeElement(local_addr); + if(comm.not_seen.size() > 0) { // forward gossip + Vector v=Util.pickSubset(comm.not_seen, subset); + out.reset(); + os=new ObjectOutputStream(out); + os.writeObject(comm); + os.flush(); + for(int i=0; i < v.size(); i++) { + channel.send(new Message((Address)v.elementAt(i), null, out.toByteArray())); + } + } + } + break; + case Command.CLEAR: + clearPanel(); + continue; + default: + System.err.println("***** Gossip.run(): received invalid draw command " + comm.mode); + break; + } + + } + catch(ChannelNotConnectedException not) { + System.err.println("Gossip: " + not); + break; + } + catch(ChannelClosedException closed) { + System.err.println("Gossip: channel was closed"); + break; + } + catch(Exception e) { + System.err.println(e); + continue; // break; + } + } + } + + + /* --------------- Callbacks --------------- */ + + + public void mouseMoved(MouseEvent e) { + } + + + public void clearPanel() { + Rectangle bounds=null; + if(panel == null || graphics == null) + return; + + bounds=panel.getBounds(); + graphics.clearRect(0, 0, bounds.width, bounds.height); + } + + + public void colorPanel(int r, int g, int b) { + if(graphics != null) { + red=r; + green=g; + blue=b; + graphics.setColor(new Color(red, green, blue)); + Rectangle bounds=panel.getBounds(); + graphics.fillRect(0, 0, bounds.width, bounds.height); + graphics.setColor(default_color); + } + } + + + void sendGossip() { + int tmp[]=new int[1]; + tmp[0]=0; + Command comm; + ObjectOutputStream os; + Vector dests=(Vector)members.clone(); + + try { + selectColor(); // set a new randomly chosen color + dests.removeElement(local_addr); + dests=Util.pickSubset(dests, subset); + if(dests == null || dests.size() == 0) { // only apply new color locally + // System.out.println("-- local"); + colorPanel(red, green, blue); + return; + } + + colorPanel(red, green, blue); + comm=new Command(Command.GOSSIP, red, green, blue); + comm.not_seen=(Vector)members.clone(); + comm.not_seen.removeElement(local_addr); + out.reset(); + os=new ObjectOutputStream(out); + os.writeObject(comm); + os.flush(); + for(int i=0; i < dests.size(); i++) { + channel.send(new Message((Address)dests.elementAt(i), null, out.toByteArray())); + } + } + catch(Exception ex) { + System.err.println(ex); + } + } + + + public void sendClearPanelMsg() { + int tmp[]=new int[1]; + tmp[0]=0; + Command comm=new Command(Command.CLEAR); + ObjectOutputStream os; + + try { + out.reset(); + os=new ObjectOutputStream(out); + os.writeObject(comm); + os.flush(); + channel.send(new Message(null, null, out.toByteArray())); + } + catch(Exception ex) { + System.err.println(ex); + } + } + + + public void windowActivated(WindowEvent e) { + } + + public void windowClosed(WindowEvent e) { + } + + public void windowClosing(WindowEvent e) { + System.exit(0); // exit the dirty way ... + } + + public void windowDeactivated(WindowEvent e) { + } + + public void windowDeiconified(WindowEvent e) { + } + + public void windowIconified(WindowEvent e) { + } + + public void windowOpened(WindowEvent e) { + } + + + public void actionPerformed(ActionEvent e) { + String command=e.getActionCommand(); + if("Gossip".equals(command)) { + sendGossip(); + } + else + if("Clear".equals(command)) + sendClearPanelMsg(); + else + if("Leave & Exit".equals(command)) { + try { + channel.disconnect(); + channel.close(); + } + catch(Exception ex) { + System.err.println(ex); + } + mainFrame.setVisible(false); + System.exit(0); + } + else + System.out.println("Unknown action"); + } + + + public void channelConnected(Channel channel) { + if(first) + first=false; + else + startThread(); + } + + public void channelDisconnected(Channel channel) { + // System.out.println("----> channelDisconnected()"); + } + + public void channelClosed(Channel channel) { + // System.out.println("----> channelClosed()"); + } + + public void channelShunned() { + System.out.println("----> channelShunned()"); + } + + public void channelReconnected(Address new_addr) { + System.out.println("----> channelReconnected(" + new_addr + ')'); + local_addr=new_addr; + } + + + private static class Command implements Serializable { + static final int GOSSIP=1; + static final int CLEAR=2; + final int mode; + int r=0; + int g=0; + int b=0; + Vector not_seen=new Vector(); + + Command(int mode) { + this.mode=mode; + } + + Command(int mode, int r, int g, int b) { + this.mode=mode; + this.r=r; + this.g=g; + this.b=b; + } + + + public String toString() { + StringBuilder ret=new StringBuilder(); + switch(mode) { + case GOSSIP: + ret.append("GOSSIP(" + r + '|' + g + '|' + b); + break; + case CLEAR: + ret.append("CLEAR"); + break; + default: + return ""; + } + ret.append(", not_seen=" + not_seen); + return ret.toString(); + } + } + + + private class TrafficGenerator implements Runnable { + Thread generator=null; + + public void start() { + if(generator == null) { + generator=new Thread(this, "TrafficGeneratorThread"); + generator.start(); + } + } + + public void stop() { + if(generator != null) + generator=null; + generator=null; + } + + public void run() { + while(generator != null) { + Util.sleep(traffic_interval); + if(generator != null) + sendGossip(); + } + } + } + + + private class MyPanel extends JPanel { + final Dimension preferred_size=new Dimension(200, 200); + + public Dimension getPreferredSize() { + return preferred_size; + } + + } + + +} + + + + + + Index: 3rdParty_sources/jgroups/org/jgroups/demos/KeyStoreGenerator.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/demos/KeyStoreGenerator.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/demos/KeyStoreGenerator.java 17 Aug 2012 14:51:24 -0000 1.1 @@ -0,0 +1,133 @@ +//$Id: KeyStoreGenerator.java,v 1.1 2012/08/17 14:51:24 marcin Exp $ + +package org.jgroups.demos; + +import java.io.FileOutputStream; +import java.io.OutputStream; +import java.security.KeyStore; + +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; + +/** + * Generates a keystore file that has a SecretKey in it. It is not possible to + * use the keytool JDk tool to achieve this. This is a simple way to generate + * a JCEKS format keystore and SecretKey. + * + * Usage is --alg ALGNAME --size ALGSIZE --storeName FILENAME --storePass PASSWORD --alias KEYALIAS + * + * Any of args are optional and will default to + *

+ * + * @author S Woodcock + * + */ +public class KeyStoreGenerator { + + static String symAlg = "Blowfish"; + static int keySize =56; + static String keyStoreName = "defaultStore.keystore"; + static String storePass = "changeit"; + static String alias = "myKey"; + + public static void main(String[] args) + { + + int i = 0, j; + String arg =null;; + boolean specified =false; + + while (i < args.length && args[i].startsWith("-")) { + arg = args[i++]; + System.out.println("Found arg of " + arg); + if (arg.equalsIgnoreCase("--alg")){ + if (i "); + System.out.flush(); + line=in.readLine(); + if(line.startsWith("quit") || line.startsWith("exit")) { + bus.stop(); + bus=null; + break; + } + bus.sendNotification(line); + } + catch(Exception e) { + log.error(e); + } + } + } + catch(Exception ex) { + log.error(ex); + } + finally { + if(bus != null) + bus.stop(); + } + } + + + public void handleNotification(Serializable n) { + System.out.println("** Received notification: " + n); + //if(cache != null) + // cache.addElement(n); + //System.out.println("cache is " + cache); + } + + + public Serializable getCache() { + // return cache; + return null; + } + + + public void memberJoined(Address mbr) { + System.out.println("** Member joined: " + mbr); + } + + public void memberLeft(Address mbr) { + System.out.println("** Member left: " + mbr); + } + + + public static void main(String[] args) { + String name="BusDemo"; + String props="udp.xml"; + + for(int i=0; i < args.length; i++) { + if("-bus_name".equals(args[i])) { + name=args[++i]; + continue; + } + if("-props".equals(args[i])) { + props=args[++i]; + continue; + } + System.out.println("NotificationBusDemo [-help] [-bus_name ] " + + "[-props ]"); + return; + } + System.out.println("Starting NotificationBus with name " + name); + new NotificationBusDemo().start(name, props); + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/demos/QuoteClient.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/demos/QuoteClient.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/demos/QuoteClient.java 17 Aug 2012 14:51:24 -0000 1.1 @@ -0,0 +1,266 @@ +// $Id: QuoteClient.java,v 1.1 2012/08/17 14:51:24 marcin Exp $ + +package org.jgroups.demos; + + +import org.jgroups.*; +import org.jgroups.blocks.GroupRequest; +import org.jgroups.blocks.RpcDispatcher; +import org.jgroups.util.Rsp; +import org.jgroups.util.RspList; + +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; +import java.util.Enumeration; +import java.util.Hashtable; + + + + +/** + * Used in conjunction with QuoteServer: a client is member of a group of quote servers which replicate + * stock quotes among themselves. The client broadcasts its request (set, get quotes) and (in the case of get + * waits for the first reply received (usually the one from the quote server closest to it). The client + * can get and set quotes as long as a minimum of 1 server (in the group) is running. + * @author Bela Ban + */ +public class QuoteClient extends Frame implements WindowListener, ActionListener, + MembershipListener { + static final String channel_name="Quotes"; + RpcDispatcher disp; + Channel channel; + final Button get=new Button("Get"); + final Button set=new Button("Set"); + final Button quit=new Button("Quit"); + final Button get_all=new Button("All"); + final Label stock=new Label("Stock"); + final Label value=new Label("Value"); + final Label err_msg=new Label("Error"); + final TextField stock_field=new TextField(); + final TextField value_field=new TextField(); + final java.awt.List listbox=new java.awt.List(); + final Font default_font=new Font("Helvetica", Font.PLAIN, 12); + + final String props=null; // default stack from JChannel + + + public QuoteClient() { + super(); + try { + channel=new JChannel(props); + channel.setOpt(Channel.LOCAL, Boolean.FALSE); + disp=new RpcDispatcher(channel, null, this, this); + channel.connect(channel_name); + } + catch(Exception e) { + System.err.println("QuoteClient(): " + e); + } + addWindowListener(this); + } + + private void showMsg(String msg) { + err_msg.setText(msg); + err_msg.setVisible(true); + } + + private void clearMsg() { + err_msg.setVisible(false); + } + + + public void start() { + setLayout(null); + setSize(400, 300); + setFont(default_font); + + stock.setBounds(new Rectangle(10, 30, 60, 30)); + value.setBounds(new Rectangle(10, 60, 60, 30)); + stock_field.setBounds(new Rectangle(100, 30, 100, 30)); + value_field.setBounds(new Rectangle(100, 60, 100, 30)); + listbox.setBounds(210, 30, 150, 160); + err_msg.setBounds(new Rectangle(10, 200, 350, 30)); + err_msg.setFont(new Font("Helvetica", Font.ITALIC, 12)); + err_msg.setForeground(Color.red); + err_msg.setVisible(false); + get.setBounds(new Rectangle(10, 250, 80, 30)); + set.setBounds(new Rectangle(100, 250, 80, 30)); + quit.setBounds(new Rectangle(190, 250, 80, 30)); + get_all.setBounds(new Rectangle(280, 250, 80, 30)); + + get.addActionListener(this); + set.addActionListener(this); + quit.addActionListener(this); + get_all.addActionListener(this); + + add(stock); + add(value); + add(stock_field); + add(value_field); + add(err_msg); + add(get); + add(set); + add(quit); + add(get_all); + add(listbox); + // stock_field.requestFocus(); + setVisible(true); + } + + + public void windowActivated(WindowEvent e) { + } + + public void windowClosed(WindowEvent e) { + } + + public void windowClosing(WindowEvent e) { + System.exit(0); + } + + public void windowDeactivated(WindowEvent e) { + } + + public void windowDeiconified(WindowEvent e) { + } + + public void windowIconified(WindowEvent e) { + } + + public void windowOpened(WindowEvent e) { + } + + + public void actionPerformed(ActionEvent e) { + String command=e.getActionCommand(); + RspList rsp_list; + Rsp first_rsp; + + try { + if(command.equals("Get")) { + String stock_name=stock_field.getText(); + if(stock_name == null || stock_name.length() == 0) { + showMsg("Stock name is empty !"); + return; + } + showMsg("Looking up value for " + stock_name + ':'); + rsp_list=disp.callRemoteMethods(null, "getQuote", new Object[]{stock_name}, + new String[]{String.class.getName()}, + GroupRequest.GET_ALL, 10000); + + Float val=null; + for(int i=0; i < rsp_list.size(); i++) { + Rsp rsp=(Rsp)rsp_list.elementAt(i); + Object obj=rsp.getValue(); + if(obj == null || obj instanceof Throwable) + continue; + val=(Float)obj; + break; + } + + if(val != null) { + value_field.setText(val.toString()); + clearMsg(); + } + else { + value_field.setText(""); + showMsg("Value for " + stock_name + " not found"); + } + } + else + if(command.equals("Set")) { + String stock_name=stock_field.getText(); + String stock_val=value_field.getText(); + if(stock_name == null || stock_val == null || stock_name.length() == 0 || + stock_val.length() == 0) { + showMsg("Stock name and value have to be present to enter a new value"); + return; + } + Float val=new Float(stock_val); + disp.callRemoteMethods(null, "setQuote", new Object[]{stock_name, val}, + new Class[]{String.class, Float.class}, + GroupRequest.GET_FIRST, 0); + + showMsg("Stock " + stock_name + " set to " + val); + } + else + if(command.equals("All")) { + listbox.removeAll(); + showMsg("Getting all stocks:"); + rsp_list=disp.callRemoteMethods(null, "getAllStocks", + (Object[])null, (Class[])null, + GroupRequest.GET_ALL, 5000); + + System.out.println("rsp_list is " + rsp_list); + + Hashtable all_stocks=null; + for(int i=0; i < rsp_list.size(); i++) { + Rsp rsp=(Rsp)rsp_list.elementAt(i); + Object obj=rsp.getValue(); + if(obj == null || obj instanceof Throwable) + continue; + all_stocks=(Hashtable)obj; + break; + } + + if(all_stocks == null) { + showMsg("No stocks found"); + return; + } + clearMsg(); + listbox.removeAll(); + String key; + Float val; + for(Enumeration en=all_stocks.keys(); en.hasMoreElements();) { + key=(String)en.nextElement(); + val=(Float)all_stocks.get(key); + if(val == null) + continue; + listbox.add(key + ": " + val.toString()); + } + } + else + if(command.equals("Quit")) { + setVisible(false); + channel.close(); + System.exit(0); + } + else + System.out.println("Unknown action"); + } + catch(Exception ex) { + value_field.setText(""); + ex.printStackTrace(); + showMsg(ex.toString()); + } + } + + + + public void setQuote(String stock_name, Float value) { + ; + } + + public void printAllStocks() { + } + + + public void viewAccepted(View new_view) { + setTitle("Members in " + channel_name + ": " + (new_view.size() - 1)); + } + + public void suspect(Address suspected_mbr) { + } + + public void block() { + } + + + public static void main(String args[]) { + QuoteClient client=new QuoteClient(); + client.start(); + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/demos/QuoteServer.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/demos/QuoteServer.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/demos/QuoteServer.java 17 Aug 2012 14:51:24 -0000 1.1 @@ -0,0 +1,137 @@ +// $Id: QuoteServer.java,v 1.1 2012/08/17 14:51:24 marcin Exp $ + +package org.jgroups.demos; + + +import org.jgroups.*; +import org.jgroups.blocks.RpcDispatcher; +import org.jgroups.util.Util; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.Date; +import java.util.Enumeration; +import java.util.Hashtable; + + + + +/** + * Example of a replicated quote server. The server maintains state which consists of a list + * of quotes and their corresponding values. When it is started, it tries to reach other + * quote servers to get its initial state. If it does not receive any response after 5 + * seconds, it assumes it is the first server and starts processing requests.

+ * Any updates are multicast across the cluster + * @author Bela Ban + */ + +public class QuoteServer implements MembershipListener, MessageListener { + final Hashtable stocks=new Hashtable(); + Channel channel; + RpcDispatcher disp; + static final String channel_name="Quotes"; + final int num_members=1; + Log log=LogFactory.getLog(getClass()); + + final String props=null; // default stack from JChannel + + private void integrate(Hashtable state) { + String key; + if(state == null) + return; + for(Enumeration e=state.keys(); e.hasMoreElements();) { + key=(String)e.nextElement(); + stocks.put(key, state.get(key)); // just overwrite + } + } + + public void viewAccepted(View new_view) { + System.out.println("Accepted view (" + new_view.size() + new_view.getMembers() + ')'); + } + + public void suspect(Address suspected_mbr) { + } + + public void block() { + } + + public void start() { + try { + channel=new JChannel(props); + disp=new RpcDispatcher(channel, this, this, this); + channel.connect(channel_name); + System.out.println("\nQuote Server started at " + new Date()); + System.out.println("Joined channel '" + channel_name + "' (" + channel.getView().size() + " members)"); + channel.getState(null, 0); + System.out.println("Ready to serve requests"); + } + catch(Exception e) { + log.error("QuoteServer.start() : " + e); + System.exit(-1); + } + } + + /* Quote methods: */ + + public float getQuote(String stock_name) throws Exception { + System.out.print("Getting quote for " + stock_name + ": "); + Float retval=(Float)stocks.get(stock_name); + if(retval == null) { + System.out.println("not found"); + throw new Exception("Stock " + stock_name + " not found"); + } + System.out.println(retval.floatValue()); + return retval.floatValue(); + } + + public void setQuote(String stock_name, Float value) { + System.out.println("Setting quote for " + stock_name + ": " + value); + stocks.put(stock_name, value); + } + + public Hashtable getAllStocks() { + System.out.print("getAllStocks: "); + printAllStocks(); + return stocks; + } + + public void printAllStocks() { + System.out.println(stocks); + } + + public void receive(Message msg) { + } + + public byte[] getState() { + try { + return Util.objectToByteBuffer(stocks.clone()); + } + catch(Exception ex) { + ex.printStackTrace(); + return null; + } + } + + public void setState(byte[] state) { + try { + integrate((Hashtable)Util.objectFromByteBuffer(state)); + } + catch(Exception ex) { + ex.printStackTrace(); + } + } + + public static void main(String args[]) { + try { + QuoteServer server=new QuoteServer(); + server.start(); + while(true) { + Util.sleep(10000); + } + } + catch(Throwable t) { + t.printStackTrace(); + } + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/demos/ReplicatedHashMapDemo.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/demos/ReplicatedHashMapDemo.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/demos/ReplicatedHashMapDemo.java 17 Aug 2012 14:51:24 -0000 1.1 @@ -0,0 +1,267 @@ + + +package org.jgroups.demos; + + +import org.jgroups.*; +import org.jgroups.blocks.ReplicatedHashMap; +import org.jgroups.persistence.PersistenceFactory; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; +import java.util.Map; +import java.util.Vector; +import java.io.Serializable; + + +/** + * Uses the ReplicatedHashMap building block, which subclasses java.util.HashMap and overrides + * the methods that modify the hashmap (e.g. put()). Those methods are multicast to the group, whereas + * read-only methods such as get() use the local copy. A ReplicatedtHashMap is created given the name + * of a group; all hashmaps with the same name find each other and form a group. + * @author Bela Ban + * @version $Id: ReplicatedHashMapDemo.java,v 1.1 2012/08/17 14:51:24 marcin Exp $ + */ +public class ReplicatedHashMapDemo extends Frame implements WindowListener, ActionListener, + ReplicatedHashMap.Notification { + static final String groupname="HashMapDemo"; + ReplicatedHashMap map=null; + final JButton get=new JButton("Get"); + final JButton set=new JButton("Set"); + final JButton quit=new JButton("Quit"); + final JButton get_all=new JButton("All"); + final JButton delete=new JButton("Delete"); + final JLabel stock=new JLabel("Key"); + final JLabel value=new JLabel("Value"); + final JLabel err_msg=new JLabel("Error"); + final JTextField stock_field=new JTextField(); + final JTextField value_field=new JTextField(); + final List listbox=new List(); + final Font default_font=new Font("Helvetica", Font.PLAIN,12); + + + + + public ReplicatedHashMapDemo() { + super(); + addWindowListener(this); + } + + private void showMsg(String msg) { + err_msg.setText(msg); + err_msg.setVisible(true); + } + + private void clearMsg() {err_msg.setVisible(false);} + + + private void removeItem() { + int index=listbox.getSelectedIndex(); + if(index == -1) { + showMsg("No item selected in listbox to be deleted !"); + return; + } + String s=listbox.getSelectedItem(); + String key=s.substring(0, s.indexOf(':', 0)); + if(key != null) + map.remove(key); + } + + private void showAll() { + if(listbox.getItemCount() > 0) + listbox.removeAll(); + if(map.isEmpty()) + return; + clearMsg(); + String key; + Float val; + + for(Map.Entry entry: map.entrySet()) { + key=entry.getKey(); + val=entry.getValue(); + if(val == null) + continue; + listbox.add(key + ": " + val.toString()); + } + } + + + + + + public void start(ChannelFactory factory, String props, boolean persist) + throws ChannelException { + map=new ReplicatedHashMap(groupname, factory, props, persist, 10000); + map.addNotifier(this); + + setLayout(null); + setSize(400, 300); + setFont(default_font); + + stock.setBounds(new Rectangle(10, 30, 60, 30)); + value.setBounds(new Rectangle(10, 60, 60, 30)); + stock_field.setBounds(new Rectangle(100, 30, 100, 30)); + value_field.setBounds(new Rectangle(100, 60, 100, 30)); + listbox.setBounds(new Rectangle(210, 30, 150, 160)); + err_msg.setBounds(new Rectangle(10, 200, 350, 30)); + err_msg.setFont(new Font("Helvetica",Font.ITALIC,12)); + err_msg.setForeground(Color.red); + err_msg.setVisible(false); + get.setBounds(new Rectangle(10, 250, 60, 30)); + set.setBounds(new Rectangle(80, 250, 60, 30)); + quit.setBounds(new Rectangle(150, 250, 60, 30)); + get_all.setBounds(new Rectangle(220, 250, 60, 30)); + delete.setBounds(new Rectangle(290, 250, 80, 30)); + + get.addActionListener(this); + set.addActionListener(this); + quit.addActionListener(this); + get_all.addActionListener(this); + delete.addActionListener(this); + + add(stock); add(value); + add(stock_field); add(value_field); + add(err_msg); + add(get); add(set); add(quit); add(get_all); add(delete); + add(listbox); + _setTitle(); + showAll(); + setVisible(true); + } + + + + + public void windowActivated(WindowEvent e) {} + public void windowClosed(WindowEvent e) {} + public void windowClosing(WindowEvent e) {System.exit(0);} + public void windowDeactivated(WindowEvent e) {} + public void windowDeiconified(WindowEvent e) {} + public void windowIconified(WindowEvent e) {} + public void windowOpened(WindowEvent e) {} + + + public void actionPerformed(ActionEvent e) { + String command=e.getActionCommand(); + try { + if(command.equals("Get")) { + String stock_name=stock_field.getText(); + if(stock_name == null || stock_name.length() == 0) { + showMsg("Key is empty !"); + return; + } + showMsg("Looking up value for " + stock_name + ':'); + Float val=map.get(stock_name); + if(val != null) { + value_field.setText(val.toString()); + clearMsg(); + } + else { + value_field.setText(""); + showMsg("Value for " + stock_name + " not found"); + } + } + else if(command.equals("Set")) { + String stock_name=stock_field.getText(); + String stock_val=value_field.getText(); + if(stock_name == null || stock_val == null || stock_name.length() == 0 || + stock_val.length() == 0) { + showMsg("Both key and value have to be present to create a new entry"); + return; + } + Float val=new Float(stock_val); + map.put(stock_name, val); + showMsg("Key " + stock_name + " set to " + val); + } + else if(command.equals("All")) { + showAll(); + } + else if(command.equals("Quit")) { + setVisible(false); + System.exit(0); + } + else if(command.equals("Delete")) + removeItem(); + else + System.out.println("Unknown action"); + } + catch(Exception ex) { + value_field.setText(""); + showMsg(ex.toString()); + } + } + + public void entrySet(Serializable key, Serializable value) { + showAll(); + } + + public void entryRemoved(Serializable key) { + showAll(); + } + + public void contentsSet(Map m) { + System.out.println("new contents: " + m); + } + + public void contentsCleared() { + System.out.println("contents cleared"); + } + + public void viewChange(View view, Vector new_mbrs, Vector old_mbrs) { + System.out.println("** view: " + view); + _setTitle(); + } + + + private void _setTitle() { + int num=map.getChannel().getView().getMembers().size(); + setTitle("ReplicatedHashMapDemo: " + num + " server(s)"); + } + + public static void main(String args[]) { + ReplicatedHashMapDemo client=new ReplicatedHashMapDemo(); + ChannelFactory factory=new JChannelFactory(); + String arg; + boolean persist=false; + + + String props="udp.xml"; + + try { + for(int i=0; i < args.length; i++) { + arg=args[i]; + if("-persist".equals(arg) && i+1]"); + } + +} \ No newline at end of file Index: 3rdParty_sources/jgroups/org/jgroups/demos/ReplicatedTreeDemo.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/demos/ReplicatedTreeDemo.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/demos/ReplicatedTreeDemo.java 17 Aug 2012 14:51:24 -0000 1.1 @@ -0,0 +1,702 @@ +// $Id: ReplicatedTreeDemo.java,v 1.1 2012/08/17 14:51:24 marcin Exp $ + +package org.jgroups.demos; + + +import org.jgroups.View; +import org.jgroups.blocks.ReplicatedTree; + +import javax.swing.*; +import javax.swing.event.TableModelEvent; +import javax.swing.event.TableModelListener; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; +import javax.swing.table.DefaultTableModel; +import javax.swing.table.TableColumn; +import javax.swing.tree.*; +import java.awt.*; +import java.awt.event.*; +import java.io.File; +import java.util.*; + + +/** + * Graphical view of a ReplicatedTree + * + * @author Bela Ban + */ +public class ReplicatedTreeDemo { + + + /** + * Graphical view of a ReplicatedTree (using the MVC paradigm). An instance of this class needs to be given a + * reference to the underlying model (ReplicatedTree) and needs to registers as a ReplicatedTreeListener. Changes + * to the tree structure are propagated from the model to the view (via ReplicatedTreeListener), changes from the + * GUI (e.g. by a user) are executed on the tree model (which will broadcast the changes to all replicas).

+ * The view itself caches only the nodes, but doesn't cache any of the data (HashMap) associated with it. When + * data needs to be displayed, the underlying tree will be accessed directly. + * + * @author Bela Ban + */ + static class ReplicatedTreeView extends JFrame implements WindowListener, ReplicatedTree.ReplicatedTreeListener, + TreeSelectionListener, TableModelListener { + DefaultTreeModel tree_model=null; + JTree jtree=null; + final DefaultTableModel table_model=new DefaultTableModel(); + final JTable table=new JTable(table_model); + final MyNode root=new MyNode(SEP); + final String props=null; + String selected_node=null; + ReplicatedTree tree=null; // the underlying model + JPanel tablePanel=null; + JMenu operationsMenu=null; + JPopupMenu operationsPopup=null; + JMenuBar menubar=null; + static final String SEP=ReplicatedTree.SEPARATOR; + private static final int KEY_COL_WIDTH=20; + private static final int VAL_COL_WIDTH=300; + + + public ReplicatedTreeView(ReplicatedTree tree, Object title) throws Exception { + this.tree=tree; + tree.addReplicatedTreeListener(this); + + addNotify(); + setTitle("ReplicatedTreeDemo: mbr=" + title); + + tree_model=new DefaultTreeModel(root); + jtree=new JTree(tree_model); + jtree.setDoubleBuffered(true); + jtree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); + + JScrollPane scroll_pane=new JScrollPane(jtree); + + populateTree(); + + getContentPane().add(scroll_pane, BorderLayout.CENTER); + addWindowListener(this); + + table_model.setColumnIdentifiers(new String[]{"Name", "Value"}); + table_model.addTableModelListener(this); + + setTableColumnWidths(); + + tablePanel=new JPanel(); + tablePanel.setLayout(new BorderLayout()); + tablePanel.add(table.getTableHeader(), BorderLayout.NORTH); + tablePanel.add(table, BorderLayout.CENTER); + + getContentPane().add(tablePanel, BorderLayout.SOUTH); + + jtree.addTreeSelectionListener(this);//REVISIT + + MouseListener ml=new MouseAdapter() { + public void mouseClicked(MouseEvent e) { + int selRow=jtree.getRowForLocation(e.getX(), e.getY()); + TreePath selPath=jtree.getPathForLocation(e.getX(), e.getY()); + if(selRow != -1) { + selected_node=makeFQN(selPath.getPath()); + jtree.setSelectionPath(selPath); + + if(e.getModifiers() == java.awt.event.InputEvent.BUTTON3_MASK) { + operationsPopup.show(e.getComponent(), + e.getX(), e.getY()); + } + } + } + }; + + jtree.addMouseListener(ml); + + createMenus(); + setLocation(50, 50); + setSize(getInsets().left + getInsets().right + 485, + getInsets().top + getInsets().bottom + 367); + + init(); + setVisible(true); + } + + public void windowClosed(WindowEvent event) { + } + + public void windowDeiconified(WindowEvent event) { + } + + public void windowIconified(WindowEvent event) { + } + + public void windowActivated(WindowEvent event) { + } + + public void windowDeactivated(WindowEvent event) { + } + + public void windowOpened(WindowEvent event) { + } + + public void windowClosing(WindowEvent event) { + System.exit(0); + } + + + public void tableChanged(TableModelEvent evt) { + int row, col; + String key, val; + + if(evt.getType() == TableModelEvent.UPDATE) { + row=evt.getFirstRow(); + col=evt.getColumn(); + if(col == 0) { // set() + key=(String)table_model.getValueAt(row, col); + val=(String)table_model.getValueAt(row, col + 1); + if(key != null && val != null) { + tree.put(selected_node, key, val); + } + } + else { // add() + key=(String)table_model.getValueAt(row, col - 1); + val=(String)table.getValueAt(row, col); + if(key != null && val != null) { + tree.put(selected_node, key, val); + } + } + } + } + + + public void valueChanged(TreeSelectionEvent evt) { + TreePath path=evt.getPath(); + String fqn=SEP; + String component_name; + HashMap data=null; + + for(int i=0; i < path.getPathCount(); i++) { + component_name=((MyNode)path.getPathComponent(i)).name; + if(component_name.equals(SEP)) + continue; + if(fqn.equals(SEP)) + fqn+=component_name; + else + fqn=fqn + SEP + component_name; + } + data=getData(tree, fqn); + if(data != null) { + getContentPane().add(tablePanel, BorderLayout.SOUTH); + populateTable(data); + validate(); + } + else { + clearTable(); + getContentPane().remove(tablePanel); + validate(); + } + } + + + + /* ------------------ ReplicatedTree.ReplicatedTreeListener interface ------------ */ + + public void nodeAdded(String fqn) { + MyNode n, p; + + n=root.add(fqn); + if(n != null) { + p=(MyNode)n.getParent(); + tree_model.reload(p); + jtree.scrollPathToVisible(new TreePath(n.getPath())); + } + } + + public void nodeRemoved(String fqn) { + MyNode n; + TreeNode par; + + n=root.findNode(fqn); + if(n != null) { + n.removeAllChildren(); + par=n.getParent(); + n.removeFromParent(); + tree_model.reload(par); + } + } + + public void nodeModified(String fqn) { +// HashMap data; + // data=getData(tree, fqn); + //populateTable(data); REVISIT + /* + poulateTable is the current table being shown is the info of the node. that is modified. + */ + } + + public void viewChange(View new_view) { + Vector mbrship; + if(new_view != null && (mbrship=new_view.getMembers()) != null) { + tree._put(SEP, "members", mbrship); + tree._put(SEP, "coordinator", mbrship.firstElement()); + } + } + + + + + /* ---------------- End of ReplicatedTree.ReplicatedTreeListener interface -------- */ + + /*----------------- Runnable implementation to make View change calles in AWT Thread ---*/ + + public void run() { + + } + + + + /* ----------------------------- Private Methods ---------------------------------- */ + + /** + * Fetches all data from underlying tree model and display it graphically + */ + void init() { + Vector mbrship=null; + + addGuiNode(SEP); + + mbrship=tree != null && tree.getMembers() != null ? (Vector)tree.getMembers().clone() : null; + if(mbrship != null) { + tree._put(SEP, "members", mbrship); + tree._put(SEP, "coordinator", mbrship.firstElement()); + } + } + + + /** + * Fetches all data from underlying tree model and display it graphically + */ + private void populateTree() { + addGuiNode(SEP); + } + + + /** + * Recursively adds GUI nodes starting from fqn + */ + void addGuiNode(String fqn) { + Set children; + String child_name; + + if(fqn == null) return; + + // 1 . Add myself + root.add(fqn); + + // 2. Then add my children + children=tree.getChildrenNames(fqn); + if(children != null) { + for(Iterator it=children.iterator(); it.hasNext();) { + child_name=(String)it.next(); + addGuiNode(fqn + SEP + child_name); + } + } + } + + + String makeFQN(Object[] path) { + StringBuilder sb=new StringBuilder(""); + String tmp_name; + + if(path == null) return null; + for(int i=0; i < path.length; i++) { + tmp_name=((MyNode)path[i]).name; + if(tmp_name.equals(SEP)) + continue; + else + sb.append(SEP + tmp_name); + } + tmp_name=sb.toString(); + if(tmp_name.length() == 0) + return SEP; + else + return tmp_name; + } + + void clearTable() { + int num_rows=table.getRowCount(); + + if(num_rows > 0) { + for(int i=0; i < num_rows; i++) + table_model.removeRow(0); + table_model.fireTableRowsDeleted(0, num_rows - 1); + repaint(); + } + } + + + void populateTable(HashMap data) { + String key, strval=""; + Object val; + int num_rows=0; + Map.Entry entry; + + if(data == null) return; + num_rows=data.size(); + clearTable(); + + if(num_rows > 0) { + for(Iterator it=data.entrySet().iterator(); it.hasNext();) { + entry=(Map.Entry)it.next(); + key=(String)entry.getKey(); + val=entry.getValue(); + if(val != null) strval=val.toString(); + table_model.addRow(new Object[]{key, strval}); + } + table_model.fireTableRowsInserted(0, num_rows - 1); + validate(); + } + } + + private void setTableColumnWidths() { + table.sizeColumnsToFit(JTable.AUTO_RESIZE_NEXT_COLUMN); + TableColumn column=null; + column=table.getColumnModel().getColumn(0); + column.setMinWidth(KEY_COL_WIDTH); + column.setPreferredWidth(KEY_COL_WIDTH); + column=table.getColumnModel().getColumn(1); + column.setPreferredWidth(VAL_COL_WIDTH); + } + + private void createMenus() { + menubar=new JMenuBar(); + operationsMenu=new JMenu("Operations"); + AddNodeAction addNode=new AddNodeAction(); + addNode.putValue(AbstractAction.NAME, "Add to this node"); + RemoveNodeAction removeNode=new RemoveNodeAction(); + removeNode.putValue(AbstractAction.NAME, "Remove this node"); + AddModifyDataForNodeAction addModAction=new AddModifyDataForNodeAction(); + addModAction.putValue(AbstractAction.NAME, "Add/Modify data"); + ExitAction exitAction=new ExitAction(); + exitAction.putValue(AbstractAction.NAME, "Exit"); + operationsMenu.add(addNode); + operationsMenu.add(removeNode); + operationsMenu.add(addModAction); + operationsMenu.add(exitAction); + menubar.add(operationsMenu); + setJMenuBar(menubar); + + operationsPopup=new JPopupMenu(); + operationsPopup.add(addNode); + operationsPopup.add(removeNode); + operationsPopup.add(addModAction); + } + + HashMap getData(ReplicatedTree tree, String fqn) { + HashMap data; + Set keys; + String key; + Object value; + + if(tree == null || fqn == null) return null; + keys=tree.getKeys(fqn); + if(keys == null) return null; + data=new HashMap(); + for(Iterator it=keys.iterator(); it.hasNext();) { + key=(String)it.next(); + value=tree.get(fqn, key); + if(value != null) + data.put(key, value); + } + return data; + } + + + + + /* -------------------------- End of Private Methods ------------------------------ */ + + /*----------------------- Actions ---------------------------*/ + class ExitAction extends AbstractAction { + public void actionPerformed(ActionEvent e) { + System.exit(0); + } + } + + class AddNodeAction extends AbstractAction { + public void actionPerformed(ActionEvent e) { + JTextField fqnTextField=new JTextField(); + if(selected_node != null) + fqnTextField.setText(selected_node); + Object[] information={"Enter fully qualified name", + fqnTextField}; + final String btnString1="OK"; + final String btnString2="Cancel"; + Object[] options={btnString1, btnString2}; + int userChoice=JOptionPane.showOptionDialog(null, + information, + "Add Node", + JOptionPane.YES_NO_OPTION, + JOptionPane.PLAIN_MESSAGE, + null, + options, + options[0]); + if(userChoice == 0) { + String userInput=fqnTextField.getText(); + tree.put(userInput, null); + } + } + } + + class RemoveNodeAction extends AbstractAction { + public void actionPerformed(ActionEvent e) { + tree.remove(selected_node); + } + } + + class AddModifyDataForNodeAction extends AbstractAction { + public void actionPerformed(ActionEvent e) { + HashMap data=getData(tree, selected_node); + if(data != null) { + } + else { + clearTable(); + data=new HashMap(); + data.put("Add Key", "Add Value"); + + } + populateTable(data); + getContentPane().add(tablePanel, BorderLayout.SOUTH); + validate(); + + } + } + + +// public static void main(String args[]) { +// ReplicatedTree tree; +// +// for(int i=0; i < args.length; i++) { +// if(args[i].equals("-help")) { +// System.out.println("ReplicatedTreeView [-help]"); +// return; +// } +// } +// +// try { +// tree=new ReplicatedTree(null); +// tree.setRemoteCalls(false); +// HashMap map=new HashMap(); +// map.put("name", "Framework"); +// map.put("pid", new Integer(322649)); +// tree.put("/federations/fed1/servers/Framework", map); +// tree.put("/federations/fed1/servers/Security", null); +// +// // demo.setVisible(true); +// new ReplicatedTreeView(tree, ""); +// +// tree.put("/federations/fed1/servers/Security/components/RuntimeMonitor", null); +// tree.put("/federations/fed1/servers/fenics", null); +// +// +// } +// catch(Exception ex) { +// ex.printStackTrace(System.err); +// } +// } + + + class MyNode extends DefaultMutableTreeNode { + String name=""; + + + MyNode(String name) { + this.name=name; + } + + + /** + * Adds a new node to the view. Intermediary nodes will be created if they don't yet exist. + * Returns the first node that was created or null if node already existed + */ + public MyNode add(String fqn) { + MyNode curr, n, ret=null; + StringTokenizer tok; + String child_name; + + if(fqn == null) return null; + curr=this; + tok=new StringTokenizer(fqn, ReplicatedTreeView.SEP); + + while(tok.hasMoreTokens()) { + child_name=tok.nextToken(); + n=curr.findChild(child_name); + if(n == null) { + n=new MyNode(child_name); + if(ret == null) ret=n; + curr.add(n); + } + curr=n; + } + return ret; + } + + + /** + * Removes a node from the view. Child nodes will be removed as well + */ + public void remove(String fqn) { + removeFromParent(); + } + + + MyNode findNode(String fqn) { + MyNode curr, n; + StringTokenizer tok; + String child_name; + + if(fqn == null) return null; + curr=this; + tok=new StringTokenizer(fqn, ReplicatedTreeView.SEP); + + while(tok.hasMoreTokens()) { + child_name=tok.nextToken(); + n=curr.findChild(child_name); + if(n == null) + return null; + curr=n; + } + return curr; + } + + + MyNode findChild(String relative_name) { + MyNode child; + + if(relative_name == null || getChildCount() == 0) + return null; + for(int i=0; i < getChildCount(); i++) { + child=(MyNode)getChildAt(i); + if(child.name == null) { + continue; + } + + if(child.name.equals(relative_name)) + return child; + } + return null; + } + + + String print(int indent) { + StringBuilder sb=new StringBuilder(); + + for(int i=0; i < indent; i++) + sb.append(' '); + if(!isRoot()) { + if(name == null) + sb.append("/"); + else { + sb.append(ReplicatedTreeView.SEP + name); + } + } + sb.append('\n'); + if(getChildCount() > 0) { + if(isRoot()) + indent=0; + else + indent+=4; + for(int i=0; i < getChildCount(); i++) + sb.append(((MyNode)getChildAt(i)).print(indent)); + } + return sb.toString(); + } + + + public String toString() { + return name; + } + + } + + + } + + + public static void main(String args[]) { + ReplicatedTree tree; + String start_directory=null; + boolean jmx=false; + + String props="udp.xml"; + + + for(int i=0; i < args.length; i++) { + if("-props".equals(args[i])) { + props=args[++i]; + continue; + } + if("-start_directory".equals(args[i])) { + start_directory=args[++i]; + continue; + } + if("-jmx".equals(args[i])) { + jmx=true; + continue; + } + help(); + return; + } + + try { + tree=new ReplicatedTree("ReplicatedTreeDemo-Group", props, 10000, jmx); + new ReplicatedTreeView(tree, tree.getLocalAddress()); + // demo.setVisible(true); + + if(start_directory != null && start_directory.length() > 0) { + populateTree(tree, start_directory); + } + else { + /* + HashMap map=new HashMap(); + map.put("name", "Framework"); + map.put("pid", new Integer(322649)); + tree.put("/federations/fed1/servers/Framework", map); + tree.put("/federations/fed1/servers/Security", null); + tree.put("/federations/fed1/servers/Security/components/RuntimeMonitor", null); + tree.put("/federations/fed1/servers/fenics", null); + */ + } + } + catch(Exception ex) { + ex.printStackTrace(System.err); + } + } + + + static void help() { + System.out.println("ReplicatedTreeView [-help] " + + "[-props ] [-start_directory ] [-jmx]"); + } + + static void populateTree(ReplicatedTree tree, String dir) { + File file=new File(dir); + + if(!file.exists()) return; + tree.put(dir, null); + + if(file.isDirectory()) { + String[] children=file.list(); + if(children != null && children.length > 0) { + for(int i=0; i < children.length; i++) + populateTree(tree, dir + '/' + children[i]); + } + } + } + + +} + + + + + + + + Index: 3rdParty_sources/jgroups/org/jgroups/demos/Topology.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/demos/Topology.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/demos/Topology.java 17 Aug 2012 14:51:23 -0000 1.1 @@ -0,0 +1,239 @@ +// $Id: Topology.java,v 1.1 2012/08/17 14:51:23 marcin Exp $ + + +package org.jgroups.demos; + + +import org.jgroups.*; +import org.jgroups.blocks.PullPushAdapter; + +import java.awt.*; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; +import java.util.Vector; + + + + +/** + * Demonstrates the membership service. Each member is represented by a rectangle that contains the + * addresses of all the members. The coordinator (= oldest member in the group) is painted in blue. + * New members can be started; all existing members will update their graphical appearance to reflect + * the new membership. When the coordinator itself is killed, another one will take over (the next in rank).

+ * A nice demo is to start a number of Topology instances at the same time. All of them will be blue (all are + * coordinators since they don't find each other). Then the MERGE2 protocol sets in and only one will retain + * its coordinator role. + * @todo Needs to be ported to Swing. + * @author Bela Ban + */ +public class Topology extends Frame implements WindowListener, MembershipListener { + private final Vector members=new Vector(); + private final Font myFont; + private final FontMetrics fm; + private final Color node_color=new Color(250, 220, 100); + private boolean coordinator=false; + private static final int NormalStyle=0; + private static final int CheckStyle=1; + private Channel channel; + private Object my_addr=null; + private static final String channel_name="FD-Heartbeat"; + + + public Topology() { + addWindowListener(this); + //g=getGraphics(); + fm=getFontMetrics(new Font("Helvetica", Font.PLAIN, 12)); + myFont=new Font("Helvetica", Font.PLAIN, 12); + + } + + + public void addNode(Object member) { + Object tmp; + for(int i=0; i < members.size(); i++) { + tmp=members.elementAt(i); + if(member.equals(tmp)) + return; + } + members.addElement(member); + repaint(); + } + + + public void removeNode(Object member) { + Object tmp; + for(int i=0; i < members.size(); i++) { + tmp=members.elementAt(i); + if(member.equals(tmp)) { + members.removeElement(members.elementAt(i)); + break; + } + } + repaint(); + } + + + public void drawNode(Graphics g, int x, int y, String label, int style) { + Color old=g.getColor(); + int width, height; + width=fm.stringWidth(label) + 10; + height=fm.getHeight() + 5; + + g.setColor(node_color); + + g.fillRect(x, y, width, height); + g.setColor(old); + g.drawString(label, x + 5, y + 15); + g.drawRoundRect(x - 1, y - 1, width + 1, height + 1, 10, 10); + if(style == CheckStyle) { + g.drawRoundRect(x - 2, y - 2, width + 2, height + 2, 10, 10); + g.drawRoundRect(x - 3, y - 3, width + 3, height + 3, 10, 10); + } + } + + + public void drawTopology(Graphics g) { + int x=20, y=50; + String label; + Dimension box=getSize(); + Color old=g.getColor(); + + if(coordinator) { + g.setColor(Color.cyan); + g.fillRect(11, 31, box.width - 21, box.height - 61); + g.setColor(old); + } + + g.drawRect(10, 30, box.width - 20, box.height - 60); + g.setFont(myFont); + + for(int i=0; i < members.size(); i++) { + label=members.elementAt(i).toString(); + drawNode(g, x, y, label, NormalStyle); + y+=50; + } + + + } + + + public void paint(Graphics g) { + drawTopology(g); + } + + + /* ------------ Callbacks ------------- */ + + + public void viewAccepted(View view) { + setState(view.getMembers()); + } + + public void suspect(Address suspected_mbr) { + } + + public void block() { + } + + + public void setState(Vector mbrs) { + members.removeAllElements(); + for(int i=0; i < mbrs.size(); i++) + addNode(mbrs.elementAt(i)); + if(mbrs.size() <= 1 || (mbrs.size() > 1 && mbrs.elementAt(0).equals(my_addr))) + coordinator=true; + else + coordinator=false; + repaint(); + } + + + public void coordinatorChosen() { + coordinator=true; + repaint(); + } + + + public void windowActivated(WindowEvent e) { + } + + public void windowClosed(WindowEvent e) { + } + + public void windowClosing(WindowEvent e) { + channel.close(); + System.exit(0); + } + + public void windowDeactivated(WindowEvent e) { + } + + public void windowDeiconified(WindowEvent e) { + } + + public void windowIconified(WindowEvent e) { + } + + public void windowOpened(WindowEvent e) { + } + + + public void start() throws Exception { + + //String props="TCP:" + + // "TCPPING(timeout=2000;num_initial_members=1;port_range=3;" + + // "initial_hosts=localhost[8880]):" + + // "FD:STABLE:NAKACK:FLUSH:GMS(join_timeout=12000):VIEW_ENFORCER:QUEUE"; + + + // test for pbcast + //String props="UDP:PING:FD:" + + // "pbcast.PBCAST:UNICAST:FRAG:pbcast.GMS:" + + // "STATE_TRANSFER:QUEUE"; + + + // test for pbcast + //String props="TCP:TCPPING(port_range=2;initial_hosts=daddy[8880],terrapin[8880]):FD:" + + // "pbcast.PBCAST:UNICAST:FRAG:pbcast.GMS:" + + // "STATE_TRANSFER:QUEUE"; + + + // test2 for pbcast + //String props="UDP:PING:FD:" + + // "pbcast.PBCAST:UNICAST:FRAG:pbcast.GMS:" + + // "pbcast.STATE_TRANSFER"; + + // String props=null; // default properties + + String props="udp.xml"; + + + channel=new JChannel(props); + channel.connect(channel_name); + new PullPushAdapter(channel, this); + my_addr=channel.getLocalAddress(); + if(my_addr != null) + setTitle(my_addr.toString()); + pack(); + show(); + } + + + public static void main(String[] args) { + try { + Topology top=new Topology(); + top.setLayout(null); + top.setSize(240, 507); + top.start(); + } + catch(Exception e) { + System.err.println(e); + e.printStackTrace(); + System.exit(0); + } + } + + +} + + Index: 3rdParty_sources/jgroups/org/jgroups/demos/TotalOrder.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/demos/TotalOrder.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/demos/TotalOrder.java 17 Aug 2012 14:51:24 -0000 1.1 @@ -0,0 +1,758 @@ +// $Id: TotalOrder.java,v 1.1 2012/08/17 14:51:24 marcin Exp $ + + +package org.jgroups.demos; + +import org.jgroups.*; +import org.jgroups.util.Util; + +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.net.InetAddress; +import java.nio.ByteBuffer; + + +/** + * Originally written to be a demo for TOTAL order (code to be written by a student). In the meantime, + * it evolved into a state transfer demo. All members maintain a shared matrix and continually + * broadcast changes to be applied to a randomly chosen field (e.g. multiplication of field with new + * value, division, addition, subtraction). Each member can be started independently (starts to + * broadcast update messages to all members). When "Stop" is pressed, a stop message is broadcast to + * all members, causing them to stop sending messages. The "Clear" button clears the shared state; + * "GetState" refreshes it from the shared group state (using the state transfer protocol).

If the + * demo is to be used to show TOTAL order, then the TOTAL protocol would have to be added to the + * stack. + * + * @author Bela Ban + */ +public class TotalOrder extends Frame { + final Font def_font=new Font("Helvetica", Font.BOLD, 12); + final Font def_font2=new Font("Helvetica", Font.PLAIN, 12); + MyCanvas canvas; + final MenuBar menubar=createMenuBar(); + final Button start=new Button("Start"); + final Button stop=new Button("Stop"); + final Button clear=new Button("Clear"); + final Button get_state=new Button("Get State"); + final Button quit=new Button("Quit"); + final Panel button_panel=new Panel(); + SenderThread sender=null; + ReceiverThread receiver=null; + Channel channel; + Dialog error_dlg; + long timeout=0; + int field_size=0; + int num_fields=0; + static final int x_offset=30; + static final int y_offset=40; + private int num=0; + + private int num_additions=0, num_subtractions=0, num_divisions=0, num_multiplications=0; + + + void error(String s) { + System.err.println(s); + } + + + class EventHandler extends WindowAdapter { + final Frame gui; + + public EventHandler(Frame g) { + gui=g; + } + + public void windowClosing(WindowEvent e) { + gui.dispose(); + System.exit(0); + } + } + + + class SenderThread extends Thread { + TotOrderRequest req; + boolean running=true; + + public void stopSender() { + running=false; + interrupt(); + System.out.println("-- num_additions: " + num_additions + + "\n-- num_subtractions: " + num_subtractions + + "\n-- num_divisions: " + num_divisions + + "\n-- num_multiplications: " + num_multiplications); + num_additions=num_subtractions=num_multiplications=num_divisions=0; + } + + public void run() { + this.setName("SenderThread"); + + byte[] buf; + int cnt=0; + while(running) { + try { + req=createRandomRequest(); + buf=req.toBuffer(); + channel.send(new Message(null, null, buf)); + System.out.print("-- num requests sent: " + cnt + "\r"); + if(timeout > 0) + Util.sleep(timeout); + cnt++; + if(num > 0 && cnt > num) { + running=false; + cnt=0; + } + } + catch(Exception e) { + error(e.toString()); + return; + } + } + } + } + + + class ReceiverThread extends Thread { + SetStateEvent set_state_evt; + boolean running=true; + + + public void stopReceiver() { + running=false; + interrupt(); + } + + public void run() { + this.setName("ReceiverThread"); + Message msg; + Object o; + ByteBuffer buf; + TotOrderRequest req; + while(running) { + try { + o=channel.receive(0); + if(o instanceof Message) { + try { + msg=(Message)o; + req=new TotOrderRequest(); + buf=ByteBuffer.wrap(msg.getBuffer()); + req.init(buf); + processRequest(req); + } + catch(Exception e) { + System.err.println(e); + } + } + else + if(o instanceof GetStateEvent) { + int[][] copy_of_state=canvas.getCopyOfState(); + channel.returnState(Util.objectToByteBuffer(copy_of_state)); + } + else + if(o instanceof SetStateEvent) { // state was received, set it ! + set_state_evt=(SetStateEvent)o; + canvas.setState(Util.objectFromByteBuffer(set_state_evt.getArg())); + } + else + if(o instanceof View) System.out.println(o.toString()); + } + catch(ChannelClosedException closed) { + error("Channel has been closed; receiver thread quits"); + return; + } + catch(Exception e) { + error(e.toString()); + return; + } + } + } + } + + + void processRequest(TotOrderRequest req) throws Exception { + int x=req.x, y=req.y, val=req.val; + + if(req.type == TotOrderRequest.STOP) { + stopSender(); + return; + } + + switch(req.type) { + case TotOrderRequest.ADDITION: + canvas.addValueTo(x, y, val); + num_additions++; + break; + case TotOrderRequest.SUBTRACTION: + canvas.subtractValueFrom(x, y, val); + num_subtractions++; + break; + case TotOrderRequest.MULTIPLICATION: + canvas.multiplyValueWith(x, y, val); + num_multiplications++; + break; + case TotOrderRequest.DIVISION: + canvas.divideValueBy(x, y, val); + num_divisions++; + break; + } + canvas.update(); + } + + + public TotalOrder(String title, long timeout, int num_fields, int field_size, String props, int num) { + Dimension s; + + this.timeout=timeout; + this.num_fields=num_fields; + this.field_size=field_size; + this.num=num; + setFont(def_font); + + try { + channel=new JChannel(props); + channel.connect("TotalOrderGroup"); + channel.getState(null, 8000); + } + catch(Exception e) { + e.printStackTrace(); + System.exit(-1); + } + + start.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + startSender(); + } + }); + + stop.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + try { + TotOrderRequest req=new TotOrderRequest(TotOrderRequest.STOP, 0, 0, 0); + byte[] buf=req.toBuffer(); + channel.send( + new Message( + null, + null, + buf)); + } + catch(Exception ex) { + } + } + }); + + clear.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + canvas.clear(); + } + }); + + get_state.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + try { + boolean rc=channel.getState(null, 3000); + if(rc == false) + error("State could not be retrieved !"); + } + catch(Throwable t) { + error("exception fetching state: " + t); + } + } + }); + + quit.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + channel.disconnect(); + channel.close(); + System.exit(0); + } + }); + + setTitle(title); + addWindowListener(new EventHandler(this)); + setBackground(Color.white); + setMenuBar(menubar); + + setLayout(new BorderLayout()); + canvas=new MyCanvas(num_fields, field_size, x_offset, y_offset); + + add("Center", canvas); + button_panel.setLayout(new FlowLayout()); + button_panel.setFont(def_font2); + button_panel.add(start); + button_panel.add(stop); + button_panel.add(clear); + button_panel.add(get_state); + button_panel.add(quit); + add("South", button_panel); + + s=canvas.getSize(); + s.height+=100; + setSize(s); + startReceiver(); + } + + + void startSender() { + if(sender == null || !sender.isAlive()) { + sender=new SenderThread(); + sender.start(); + } + } + + void stopSender() { + if(sender != null) { + sender.stopSender(); + sender=null; + } + } + + void startReceiver() { + if(receiver == null) { + receiver=new ReceiverThread(); + receiver.setPriority(Thread.MAX_PRIORITY); + receiver.start(); + } + } + + + + private MenuBar createMenuBar() { + MenuBar ret=new MenuBar(); + Menu file=new Menu("File"); + MenuItem quitm=new MenuItem("Quit"); + + ret.setFont(def_font2); + ret.add(file); + + file.addSeparator(); + file.add(quitm); + + + quitm.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + System.exit(1); + } + }); + return ret; + } + + + + private TotOrderRequest createRandomRequest() { + TotOrderRequest ret=null; + byte op_type=(byte)(((Math.random() * 10) % 4) + 1); // 1 - 4 + int x=(int)((Math.random() * num_fields * 2) % num_fields); + int y=(int)((Math.random() * num_fields * 2) % num_fields); + int val=(int)((Math.random() * num_fields * 200) % 10); + + ret=new TotOrderRequest(op_type, x, y, val); + return ret; + } + + + public static void main(String[] args) { + TotalOrder g; + String arg; + long timeout=200; + int num_fields=3; + int field_size=80; + String props=null; + int num=0; + + props="sequencer.xml"; + + + for(int i=0; i < args.length; i++) { + arg=args[i]; + if("-timeout".equals(arg)) { + timeout=Long.parseLong(args[++i]); + continue; + } + if("-num_fields".equals(arg)) { + num_fields=Integer.parseInt(args[++i]); + continue; + } + if("-field_size".equals(arg)) { + field_size=Integer.parseInt(args[++i]); + continue; + } + if("-help".equals(arg)) { + System.out.println("\nTotalOrder [-timeout ] [-num_fields ] " + + "[-field_size ] [-props ] [-num ]\n"); + return; + } + if("-props".equals(arg)) { + props=args[++i]; + continue; + } + if("-num".equals(arg)) { + num=Integer.parseInt(args[++i]); + } + } + + + try { + g=new TotalOrder("Total Order Demo on " + InetAddress.getLocalHost().getHostName(), + timeout, num_fields, field_size, props, num); + g.setVisible(true); + } + catch(Exception e) { + System.err.println(e); + } + } + + +} + + +class TotOrderRequest { + public static final byte STOP=0; + public static final byte ADDITION=1; + public static final byte SUBTRACTION=2; + public static final byte MULTIPLICATION=3; + public static final byte DIVISION=4; + final static int SIZE=Global.BYTE_SIZE + Global.INT_SIZE * 3; + + + public byte type=ADDITION; + public int x=0; + public int y=0; + public int val=0; + + + public TotOrderRequest() { + } + + TotOrderRequest(byte type, int x, int y, int val) { + this.type=type; + this.x=x; + this.y=y; + this.val=val; + } + + public String printType() { + switch(type) { + case STOP: + return "STOP"; + case ADDITION: + return "ADDITION"; + case SUBTRACTION: + return "SUBTRACTION"; + case MULTIPLICATION: + return "MULTIPLICATION"; + case DIVISION: + return "DIVISION"; + default: + return ""; + } + } + +// public void writeExternal(ObjectOutput out) throws IOException { +// out.writeByte(type); +// out.writeInt(x); +// out.writeInt(y); +// out.writeInt(val); +// } +// +// public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { +// type=in.readByte(); +// x=in.readInt(); +// y=in.readInt(); +// val=in.readInt(); +// } + + public byte[] toBuffer() { + ByteBuffer buf=ByteBuffer.allocate(SIZE); + buf.put(type); + buf.putInt(x); + buf.putInt(y); + buf.putInt(val); + return buf.array(); + } + + public void init(ByteBuffer buf) { + type=buf.get(); + x=buf.getInt(); + y=buf.getInt(); + val=buf.getInt(); + } + + public String toString() { + return "[" + x + ',' + y + ": " + printType() + '(' + val + ")]"; + } +} + + +class MyCanvas extends Canvas { + int field_size=100; + int num_fields=4; + int x_offset=30; + int y_offset=30; + + final Font def_font=new Font("Helvetica", Font.BOLD, 14); + int[][] array=null; // state + + Dimension off_dimension=null; + Image off_image=null; + Graphics off_graphics=null; + final Font def_font2=new Font("Helvetica", Font.PLAIN, 12); + static final Color checksum_col=Color.blue; + int checksum=0; + + + public MyCanvas(int num_fields, int field_size, int x_offset, int y_offset) { + this.num_fields=num_fields; + this.field_size=field_size; + this.x_offset=x_offset; + this.y_offset=y_offset; + + array=new int[num_fields][num_fields]; + setBackground(Color.white); + setSize(2 * x_offset + num_fields * field_size + 30, y_offset + num_fields * field_size + 50); + + for(int i=0; i < num_fields; i++) + for(int j=0; j < num_fields; j++) + array[i][j]=0; + } + + + public void setFieldSize(int fs) { + field_size=fs; + } + + public void setNumFields(int nf) { + num_fields=nf; + } + + public void setXOffset(int o) { + x_offset=o; + } + + public void setYOffset(int o) { + y_offset=o; + } + + + public void addValueTo(int x, int y, int value) { + synchronized(array) { + array[x][y]+=value; + repaint(); + } + } + + public void subtractValueFrom(int x, int y, int value) { + synchronized(array) { + array[x][y]-=value; + repaint(); + } + } + + public void multiplyValueWith(int x, int y, int value) { + synchronized(array) { + array[x][y]*=value; + repaint(); + } + } + + public void divideValueBy(int x, int y, int value) { + if(value == 0) + return; + synchronized(array) { + array[x][y]/=value; + repaint(); + } + } + + + public void setValueAt(int x, int y, int value) { + synchronized(array) { + array[x][y]=value; + } + repaint(); + } + + + public int getValueAt(int x, int y) { + synchronized(array) { + return array[x][y]; + } + } + + + public void clear() { + synchronized(array) { + for(int i=0; i < num_fields; i++) + for(int j=0; j < num_fields; j++) + array[i][j]=0; + checksum=checksum(); + repaint(); + } + } + + + public int[][] getState() { + synchronized(array) { + return array; + } + } + + + public int[][] getCopyOfState() { + int[][] retval=new int[num_fields][num_fields]; + + synchronized(array) { + for(int i=0; i < num_fields; i++) + System.arraycopy(array[i], 0, retval[i], 0, num_fields); + return retval; + } + } + + + public void update() { + checksum=checksum(); + repaint(); + } + + + public void setState(Object new_state) { + + if(new_state == null) + return; + + try { + int[][] new_array=(int[][])new_state; + synchronized(array) { + clear(); + + for(int i=0; i < num_fields; i++) + System.arraycopy(new_array[i], 0, array[i], 0, num_fields); + checksum=checksum(); + repaint(); + } + } + catch(Exception e) { + System.err.println(e); + return; + } + } + + + public int checksum() { + int retval=0; + + synchronized(array) { + for(int i=0; i < num_fields; i++) + for(int j=0; j < num_fields; j++) + retval+=array[i][j]; + } + return retval; + } + + + public void update(Graphics g) { + Dimension d=getSize(); + + if(off_graphics == null || + d.width != off_dimension.width || + d.height != off_dimension.height) { + off_dimension=d; + off_image=createImage(d.width, d.height); + off_graphics=off_image.getGraphics(); + } + + //Erase the previous image. + off_graphics.setColor(getBackground()); + off_graphics.fillRect(0, 0, d.width, d.height); + off_graphics.setColor(Color.black); + off_graphics.setFont(def_font); + drawEmptyBoard(off_graphics); + drawNumbers(off_graphics); + g.drawImage(off_image, 0, 0, this); + } + + + public void paint(Graphics g) { + update(g); + } + + + /** + * Draws the empty board, no pieces on it yet, just grid lines + */ + void drawEmptyBoard(Graphics g) { + int x=x_offset, y=y_offset; + Color old_col=g.getColor(); + + g.setFont(def_font2); + old_col=g.getColor(); + g.setColor(checksum_col); + g.drawString(("Checksum: " + checksum), x_offset + field_size, y_offset - 20); + g.setFont(def_font); + g.setColor(old_col); + + for(int i=0; i < num_fields; i++) { + for(int j=0; j < num_fields; j++) { // draws 1 row + g.drawRect(x, y, field_size, field_size); + x+=field_size; + } + g.drawString(("" + (num_fields - i - 1)), x + 20, y + field_size / 2); + y+=field_size; + x=x_offset; + } + + for(int i=0; i < num_fields; i++) { + g.drawString(("" + i), x_offset + i * field_size + field_size / 2, y + 30); + } + } + + + void drawNumbers(Graphics g) { + Point p; + String num; + FontMetrics fm=g.getFontMetrics(); + int len=0; + + synchronized(array) { + for(int i=0; i < num_fields; i++) + for(int j=0; j < num_fields; j++) { + num="" + array[i][j]; + len=fm.stringWidth(num); + p=index2Coord(i, j); + g.drawString(num, p.x - (len / 2), p.y); + } + } + } + + + Point coord2Index(int x, int y) { + Point ret=new Point(); + + ret.x=x_offset + (x * field_size); + ret.y=y_offset + ((num_fields - 1 - y) * field_size); + return ret; + } + + + Point index2Coord(int i, int j) { + int x=x_offset + i * field_size + field_size / 2; + + // int y=y_offset + j*field_size + field_size/2; + + int y=y_offset + num_fields * field_size - j * field_size - field_size / 2; + + return new Point(x, y); + } + + +} + + + + + + + + Index: 3rdParty_sources/jgroups/org/jgroups/demos/TotalTokenDemo.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/demos/TotalTokenDemo.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/demos/TotalTokenDemo.java 17 Aug 2012 14:51:24 -0000 1.1 @@ -0,0 +1,565 @@ +//$Id: TotalTokenDemo.java,v 1.1 2012/08/17 14:51:24 marcin Exp $ + +package org.jgroups.demos; + +import org.jgroups.*; +import org.jgroups.stack.Protocol; +import org.jgroups.stack.ProtocolStack; +import org.jgroups.util.Util; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.Serializable; +import java.util.Iterator; +import java.util.Random; +import java.util.Vector; + + +/** + *

+ * Demonstration of TOTAL_TOKEN protocol stack implementing total + * order. TotalTokenDemo could however be used by any other + * stack configuration which does not neccesarily have to satisfy + * total order. If using stack configuration other than TOTAL_TOKEN + * an appropriate xml configuration file should be used. See -help for + * details. + *

+ * TotalTokenDemo will verify : + *

+ * total ordering of messages - by computing a color to be displayed + * in a gui window. + *

+ * virtual synchrony - by displaying number of messages transmitted + * in last view. + *

+ * + *@author Vladimir Blagojevic vladimir@cs.yorku.ca + *@author Ivan Bilenjkij ivan@ibossa.com + *@version $Revision: 1.1 $ + * + *@see org.jgroups.protocols.TOTAL_TOKEN + * + **/ + + +public class TotalTokenDemo extends JFrame implements Runnable +{ + private JChannel channel; + //main tabbed pane + + final JTabbedPane tabbedPane; + + private ReceiverThread receiverThread; + + private ColorPanel colorPanel; + private final ControlPanel control; + private int mSize = 1024; + private volatile boolean transmitting = false; + private final String channelProperties; + private Dimension preffered; + + public TotalTokenDemo(String props) + { + super(); + tabbedPane = new JTabbedPane(); + control = new ControlPanel(); + channelProperties = props; + + try + { + channel = new JChannel(channelProperties); + } + catch (ChannelException e) + { + System.err.println("Could not create channel, exiting ...."); + e.printStackTrace(System.err); + } + + addPanel("Control", control); + getContentPane().add(tabbedPane); + connect(); + + } + public void addPanel(String name, JPanel panel) + { + + + if(tabbedPane.getTabCount() == 0) + { + preffered = panel.getPreferredSize(); + } + + + panel.setPreferredSize(preffered); + tabbedPane.add(name,panel); + } + + public JChannel getChannel() + { + return channel; + } + + + public void connect() + { + try + { + channel.connect("TOTAL_TOKEN_DEMO_GROUP"); + } + catch (ChannelException e) + { + e.printStackTrace(); + } + receiverThread = new ReceiverThread(); + receiverThread.start(); + Address a = channel.getLocalAddress(); + if(a != null) setTitle(a.toString()); + else setTitle("Not connected"); + control.connected(); + } + public void run() + { + Random r = new Random(); + + while (true) + { + Util.sleep(10); + try + { + if (transmitting) + { + channel.send(new Message(null, null, new TotalPayload(r.nextInt(255)))); + } + else + { + Util.sleep(200); + } + + } + catch (Exception e) + { + e.printStackTrace(); + } + } + } + + public void disconnect() + { + transmitting = false; + receiverThread.shutDown(); + channel.disconnect(); + control.disconnected(); + setTitle("Not connected"); + } + + private class ReceiverThread extends Thread + { + volatile boolean running = true; + Thread nullifier = null; + private long startTimeThroughput = System.currentTimeMillis(); + private final long oneSecond = 1000; + private long throughput = 1; + + public ReceiverThread() + { + nullifier = new Thread(new Runnable() + { + public void run() + { + //nullifies throughput display + while (running) + { + Util.sleep(2000); + if ((System.currentTimeMillis() - startTimeThroughput) > 2000) + { + control.throughput.setText("0 KB/sec"); + } + } + } + }); + nullifier.start(); + } + + public void shutDown() + { + running = false; + } + + private void measureThrougput(long size) + { + if ((System.currentTimeMillis() - startTimeThroughput) > oneSecond) + { + control.throughput.setText("" + (throughput / 1024) + " KB/sec"); + startTimeThroughput = System.currentTimeMillis(); + throughput = 0; + } + else + { + throughput += size; + } + } + + + public void run() + { + Object tmp; + Message msg = null; + int counter = 0; + Vector v = new Vector(); + while (running) + { + Util.sleep(10); + try + { + + tmp = channel.receive(0); + if (tmp == null) continue; + + if (tmp instanceof View) + { + View vw = (View) tmp; + control.viewNumber.setText("" + vw.getVid().getId()); + control.numMessagesInLastView.setText("" + counter); + counter = 0; + v.clear(); + continue; + } + + if (tmp instanceof ExitEvent) + { + System.out.println("received EXIT"); + break; + } + + if (!(tmp instanceof Message)) + continue; + + msg = (Message) tmp; + + measureThrougput(msg.size()); + TotalPayload p =null; + + p=(TotalPayload)msg.getObject(); + v.addElement(new Integer(p.getRandomSequence())); + int size = v.size(); + if (size % 50 == 0) + { + int value = 0; + int i = 0; + Iterator iter = v.iterator(); + while (iter.hasNext()) + { + i++; + int seq = ((Integer) iter.next()).intValue(); + if (i % 2 == 0) + { + value *= seq; + } + else if (i % 3 == 0) + { + value -= seq; + } + else + value += seq; + } + v.clear(); + value = Math.abs(value); + int r = value % 85; + int g = value % 170; + int b = value % 255; + colorPanel.setSeq(r, g, b); + } + counter++; + } + catch (ChannelNotConnectedException e) + { + e.printStackTrace(); + } + catch (ChannelClosedException e) + { + e.printStackTrace(); + } + catch (TimeoutException e) + { + e.printStackTrace(); + } + } + + } + } + + public static class TotalPayload implements Serializable + { + private final int seqRandom; + + public TotalPayload(int random) + { + seqRandom = random; + } + + public int getRandomSequence() + { + return seqRandom; + } + + } + + class TransmitAction extends AbstractAction + { + + private static final String TRANSMIT_OFF = "transmit off"; + private static final String TRANSMIT_ON = "transmit on"; + + TransmitAction() + { + putValue(NAME, TRANSMIT_OFF); + } + + public void actionPerformed(ActionEvent e) + { + if (getValue(NAME) == TRANSMIT_OFF) + { + putValue(NAME, TRANSMIT_ON); + transmitting = true; + } + else + { + putValue(NAME, TRANSMIT_OFF); + transmitting = false; + } + } + + + } + + class ControlPanel extends JPanel + { + + private static final String DISCONNECT = "Disconnect"; + private static final String CONNECT = "Connect"; + final JTextField numMessagesInLastView; + final JTextField throughput; + final JTextField viewNumber; + final JTextField messageSize; + final JTextField state; + final JButton transmit; + final JButton connectButton; + + JTabbedPane pane; + + public ControlPanel() + { + super(); + + //Layout the labels in a panel + JPanel labelPane = new JPanel(); + labelPane.setLayout(new GridLayout(0, 1)); + labelPane.add(new JLabel("Message size")); + labelPane.add(new JLabel("Current view")); + labelPane.add(new JLabel("Throughput")); + labelPane.add(new JLabel("Last view count")); + + + colorPanel = new ColorPanel(); + connectButton = new JButton(DISCONNECT); + + connectButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + JButton b = (JButton) e.getSource(); + String current_state = b.getText(); + if (CONNECT.equals(current_state)) + { + connect(); + } + else if (DISCONNECT.equals(current_state)) + { + disconnect(); + } + } + }); + + transmit = new JButton(new TransmitAction()); + labelPane.add(connectButton); + labelPane.add(transmit); + + + int size = 10; + messageSize = new JTextField(size); + messageSize.setText("" + mSize); + messageSize.addActionListener(new ActionListener() + { + /** + * Invoked when an action occurs. + */ + public void actionPerformed(ActionEvent e) + { + mSize = Integer.parseInt(messageSize.getText()); + } + }); + viewNumber = new JTextField(size); + viewNumber.setEditable(false); + throughput = new JTextField(size); + throughput.setEditable(false); + numMessagesInLastView = new JTextField(size); + numMessagesInLastView.setEditable(false); + + state = new JTextField(size); + state.setEditable(false); + + + //Layout the text fields in a panel + JPanel fieldPane = new JPanel(); + fieldPane.setLayout(new GridLayout(0, 1)); + fieldPane.add(messageSize); + fieldPane.add(viewNumber); + fieldPane.add(throughput); + fieldPane.add(numMessagesInLastView); + fieldPane.add(state); + fieldPane.add(colorPanel); + + + + //Put the panels in another panel, labels on left, + //text fields on right + JPanel contentPane = new JPanel(); + contentPane.setBorder(BorderFactory.createTitledBorder("Control")); + contentPane.setLayout(new BorderLayout()); + contentPane.add(labelPane, BorderLayout.CENTER); + contentPane.add(fieldPane, BorderLayout.EAST); + this.setLayout(new BorderLayout()); + this.add(contentPane); + } + + public void connected() + { + connectButton.setText(DISCONNECT); + state.setText("connected ok"); + } + + public void disconnected() + { + connectButton.setText(CONNECT); + state.setText("disconnected ok"); + } + } + + class ColorPanel extends JPanel + { + + long seq1,seq2,seq3; + + public ColorPanel() + { + super(); + setOpaque(false); + this.setLayout(new BorderLayout()); + //setBorder(BorderFactory.createLineBorder(Color.black)); + } + + public Dimension getPreferredSize() + { + Dimension layoutSize = super.getPreferredSize(); + int max = Math.max(layoutSize.width, layoutSize.height); + return new Dimension(max + 20, max + 20); + } + + public void setSeq(long seq1, long seq2, long seq3) + { + this.seq1 = seq1; + this.seq2 = seq2; + this.seq3 = seq3; + this.repaint(); + } + + protected void paintComponent(Graphics g) + { + Dimension size = this.getSize(); + int x = 0; + int y = 0; + g.setColor(new Color((int) seq1, (int) seq2, (int) seq3)); + g.fillRect(x, y, size.width, size.height); + } + } + class StackPanel extends JPanel + { + final ProtocolStack stack; + public StackPanel(JChannel channel) + { + super(); + setBorder(BorderFactory.createTitledBorder("ProtocolStack")); + this.setLayout(new GridLayout(0, 2)); + this.stack = channel.getProtocolStack(); + Iterator iter = stack.getProtocols().iterator(); + String debugLevels [] = new String[]{"DEBUG","INFO","ERROR"}; + while (iter.hasNext()) + { + Protocol p = (Protocol) iter.next(); + JLabel field = new JLabel(p.getName()); + JComboBox pane = new JComboBox(debugLevels); + this.add(field); + this.add(pane); + + } + } + } + + static void help() + { + System.out.println("\nTotalTokenDemo [-help] [-props ]"); + System.out.println("-props: argument can be an old-style protocol stack specification, or it can be " + + "a URL. In the latter case, the protocol specification will be read from the URL\n"); + } + + + public static void main(String args[]) + { + String props = null; + + for (int i = 0; i < args.length; i++) + { + if ("-help".equals(args[i])) + { + help(); + return; + } + if ("-props".equals(args[i])) + { + props = args[++i]; + continue; + } + help(); + return; + } + + if (props == null) + { + props = "UDP(mcast_addr=224.0.0.35;mcast_port=45566;ip_ttl=32;" + + "mcast_send_buf_size=150000;mcast_recv_buf_size=80000):" + + "PING(timeout=2000;num_initial_members=5):" + + "FD_SOCK:" + + "VERIFY_SUSPECT(timeout=1500):" + + "UNICAST(timeout=5000):" + + "FRAG(frag_size=8192;down_thread=false;up_thread=false):" + + "TOTAL_TOKEN(block_sending=50;unblock_sending=10):" + + "pbcast.GMS(join_timeout=5000;" + + "shun=false;print_local_addr=true)"; + + + } + + + + TotalTokenDemo ttd = new TotalTokenDemo(props); + //StackPanel not_done_yet = new StackPanel(ttd.getChannel()); + //ttd.addPanel("Debug", not_done_yet); + ttd.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + ttd.pack(); + ttd.show(); + new Thread(ttd).start(); + } +} + Index: 3rdParty_sources/jgroups/org/jgroups/demos/ViewDemo.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/demos/ViewDemo.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/demos/ViewDemo.java 17 Aug 2012 14:51:23 -0000 1.1 @@ -0,0 +1,93 @@ +// $Id: ViewDemo.java,v 1.1 2012/08/17 14:51:23 marcin Exp $ + +package org.jgroups.demos; + + +import org.jgroups.*; +import org.jgroups.util.Util; + +import java.util.HashMap; +import java.util.Map; + + +/** + * Demos the reception of views using a PullPushAdapter. Just start a number of members, and kill them + * randomly. The view should always be correct. + */ +public class ViewDemo extends ReceiverAdapter { + private Channel channel; + + + public void viewAccepted(View new_view) { + System.out.println("** New view: " + new_view); + } + + + /** + * Called when a member is suspected + */ + public void suspect(Address suspected_mbr) { + System.out.println("Suspected(" + suspected_mbr + ')'); + } + + + + public void start(String props, boolean use_additional_data) throws Exception { + + channel=new JChannel(props); + channel.setReceiver(this); + channel.setOpt(Channel.AUTO_RECONNECT, Boolean.TRUE); + + if(use_additional_data) { + Map m=new HashMap(); + m.put("additional_data", "bela".getBytes()); + channel.down(new Event(Event.CONFIG, m)); + } + + channel.connect("ViewDemo"); + + while(true) { + Util.sleep(10000); + } + } + + + public static void main(String args[]) { + ViewDemo t=new ViewDemo(); + boolean use_additional_data=false; + String props="udp.xml"; + + for(int i=0; i < args.length; i++) { + if("-help".equals(args[i])) { + help(); + return; + } + if("-props".equals(args[i])) { + props=args[++i]; + continue; + } + if("-use_additional_data".equals(args[i])) { + use_additional_data=Boolean.valueOf(args[++i]).booleanValue(); + continue; + } + if("-bind_addr".equals(args[i])) { + System.setProperty("jgroups.bind_addr", args[++i]); + continue; + } + help(); + return; + } + + try { + t.start(props, use_additional_data); + } + catch(Exception e) { + System.err.println(e); + } + } + + static void help() { + System.out.println("ViewDemo [-props ] [-help] [-use_additional_data ] [-bind_addr

]"); + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/demos/package.html =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/demos/package.html,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/demos/package.html 17 Aug 2012 14:51:23 -0000 1.1 @@ -0,0 +1,5 @@ + + + Provides demonstrations of JGroups functionality. + + \ No newline at end of file Index: 3rdParty_sources/jgroups/org/jgroups/demos/applets/DrawApplet.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/demos/applets/DrawApplet.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/demos/applets/DrawApplet.java 17 Aug 2012 14:51:25 -0000 1.1 @@ -0,0 +1,315 @@ +// $Id: DrawApplet.java,v 1.1 2012/08/17 14:51:25 marcin Exp $ + +package org.jgroups.demos.applets; + +import java.applet.Applet; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionListener; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.util.Random; +import java.util.Vector; +import org.jgroups.*; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + + + + +public class DrawApplet extends Applet implements Runnable, MouseMotionListener, ActionListener { + private Graphics graphics=null; + private Panel panel=null, sub_panel=null; + private final ByteArrayOutputStream out=new ByteArrayOutputStream(); + private DataOutputStream outstream; + private DataInputStream instream; + private final Random random=new Random(System.currentTimeMillis()); + private Button clear_button, leave_button; + private Label mbr_label; + private final Font default_font=new Font("Helvetica",Font.PLAIN,12); + private final String groupname="DrawGroup"; + private Channel channel=null; + private Thread receiver=null; + private int member_size=1; + private int red=0, green=0, blue=0; + private Color default_color=null; + + private final ChannelFactory factory=new JChannelFactory(); + private String props="TUNNEL(router_host=janet;router_port=12002):" + + "PING(gossip_host=janet;gossip_port=12002):" + + "FD:STABLE:NAKACK:UNICAST:FRAG:FLUSH:GMS:VIEW_ENFORCER:QUEUE"; + + private final Vector members=new Vector(); + private boolean fl=true; + Log log=LogFactory.getLog(getClass()); + + + + + + public void init() { + System.out.println("INIT"); + setLayout(new BorderLayout()); + + String tmp_props=getParameter("properties"); + if(tmp_props != null) { + System.out.println("Setting parameters " + tmp_props); + props=tmp_props; + } + + + try { + channel=factory.createChannel(props); + showStatus("Connecting to group " + groupname); + channel.connect(groupname); + } + catch(Exception e) { + log.error(e); + } + receiver=new Thread(this, "DrawThread"); + receiver.start(); + go(); + } + + + + public void start() { + System.out.println("------- START"); + } + + + + + + public void destroy() { + System.out.println("------- DESTROY"); + + if(receiver != null && receiver.isAlive()) { + fl=false; + receiver.interrupt(); + try {receiver.join(1000);} catch(Exception ex) {} + } + receiver=null; + showStatus("Disconnecting from " + groupname); + channel.disconnect(); + showStatus("Disconnected"); + } + + + public void paint(Graphics g) { + Rectangle bounds=panel.getBounds(); + Color old=graphics.getColor(); + + if(bounds == null || graphics == null) + return; + + graphics.setColor(Color.black); + graphics.drawRect(0, 0, bounds.width-1, bounds.height-1); + graphics.setColor(old); + } + + + private void selectColor() { + red=(Math.abs(random.nextInt()) % 255); + green=(Math.abs(random.nextInt()) % 255); + blue=(Math.abs(random.nextInt()) % 255); + default_color=new Color(red, green, blue); + } + + + + + public void go() { + try { + panel=new Panel(); + sub_panel=new Panel(); + resize(200, 200); + add("Center", panel); + clear_button=new Button("Clear"); + clear_button.setFont(default_font); + clear_button.addActionListener(this); + leave_button=new Button("Exit"); + leave_button.setFont(default_font); + leave_button.addActionListener(this); + mbr_label=new Label("0 mbr(s)"); + mbr_label.setFont(default_font); + sub_panel.add("South", clear_button); + sub_panel.add("South", leave_button); + sub_panel.add("South", mbr_label); + add("South", sub_panel); + panel.addMouseMotionListener(this); + setVisible(true); + mbr_label.setText(member_size + " mbrs"); + graphics=panel.getGraphics(); + selectColor(); + graphics.setColor(default_color); + panel.setBackground(Color.white); + clear_button.setForeground(Color.blue); + leave_button.setForeground(Color.blue); + } + catch(Exception e) { + log.error(e); + return; + } + } + + + public void run() { + Object tmp; + Message msg=null; + int my_x=10, my_y=10, r=0, g=0, b=0; + + + while(fl) { + my_x=10; + my_y=10; + try { + tmp=channel.receive(0); + if(tmp instanceof View) { + viewAccepted((View)tmp); + continue; + } + if(!(tmp instanceof Message)) + continue; + msg=(Message)tmp; + + if(msg == null || msg.getLength() == 0) { + log.error("DrawApplet.run(): msg or msg.buffer is null !"); + continue; + } + + instream=new DataInputStream(new ByteArrayInputStream(msg.getRawBuffer(), msg.getOffset(), msg.getLength())); + r=instream.readInt(); // red + if(r == -13) { + clearPanel(); + continue; + } + g=instream.readInt(); // green + b=instream.readInt(); // blue + my_x=instream.readInt(); + my_y=instream.readInt(); + } + catch(ChannelNotConnectedException conn) { + break; + } + catch(Exception e) { + log.error(e); + } + if(graphics != null) { + graphics.setColor(new Color(r, g, b)); + graphics.fillOval(my_x, my_y, 10, 10); + graphics.setColor(default_color); + } + } + } + + + /* --------------- Callbacks --------------- */ + + + public void mouseMoved(MouseEvent e) {} + + public void mouseDragged(MouseEvent e) { + int tmp[]=new int[1], x, y; + + tmp[0]=0; + x=e.getX(); + y=e.getY(); + + graphics.fillOval(x, y, 10, 10); + + try { + out.reset(); + outstream=new DataOutputStream(out); + outstream.writeInt(red); + outstream.writeInt(green); + outstream.writeInt(blue); + outstream.writeInt(x); + outstream.writeInt(y); + channel.send(new Message(null, null, out.toByteArray())); + out.reset(); + } + catch(Exception ex) { + log.error(ex); + } + } + + + public void clearPanel() { + Rectangle bounds=null; + if(panel == null || graphics == null) + return; + + bounds=panel.getBounds(); + graphics.clearRect(1, 1, bounds.width-2, bounds.height-2); + + + } + + + public void sendClearPanelMsg() { + int tmp[]=new int[1]; tmp[0]=0; + + clearPanel(); + + try { + out.reset(); + outstream=new DataOutputStream(out); + outstream.writeInt(-13); + channel.send(new Message(null, null, out.toByteArray())); + outstream.flush(); + } + catch(Exception ex) { + log.error(ex); + } + } + + + + + public void actionPerformed(ActionEvent e) { + String command=e.getActionCommand(); + if(command == "Clear") { + System.out.println("Members are " + members); + sendClearPanelMsg(); + } + else if(command == "Exit") { + try { + destroy(); + setVisible(false); + } + catch(Exception ex) { + log.error(ex); + } + + } + else + System.out.println("Unknown action"); + } + + + public void viewAccepted(View v) { + Vector mbrs=v.getMembers(); + if(v != null) { + System.out.println("View accepted: " +v); + member_size=v.size(); + + if(mbr_label != null) + mbr_label.setText(member_size + " mbr(s)"); + + members.removeAllElements(); + for(int i=0; i < mbrs.size(); i++) + members.addElement(mbrs.elementAt(i)); + } + } + + + + + +} + Index: 3rdParty_sources/jgroups/org/jgroups/demos/applets/draw.html =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/demos/applets/draw.html,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/demos/applets/draw.html 17 Aug 2012 14:51:25 -0000 1.1 @@ -0,0 +1,20 @@ + + + + + A Simple Drawing Program + + + + Here is an example of a distributed Draw program:
+ + + + + + + + + + Index: 3rdParty_sources/jgroups/org/jgroups/demos/applets/package.html =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/demos/applets/package.html,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/demos/applets/package.html 17 Aug 2012 14:51:25 -0000 1.1 @@ -0,0 +1,5 @@ + + + Provides an applet that demonstrates JGroups functionality. + + \ No newline at end of file Index: 3rdParty_sources/jgroups/org/jgroups/demos/wb/GraphPanel.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/demos/wb/GraphPanel.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/demos/wb/GraphPanel.java 17 Aug 2012 14:51:03 -0000 1.1 @@ -0,0 +1,363 @@ +// $Id: GraphPanel.java,v 1.1 2012/08/17 14:51:03 marcin Exp $ + + +package org.jgroups.demos.wb; + + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.Address; +import org.jgroups.blocks.GroupRequest; +import org.jgroups.blocks.MethodCall; +import org.jgroups.util.Util; + +import java.awt.*; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.util.Vector; + + + + +public class GraphPanel extends Panel implements MouseListener, MouseMotionListener { + final Whiteboard wb; + final Vector nodes=new Vector(); + final Vector copy=new Vector(); + String myname=null; + public Object my_addr=null; + Node pick; + boolean pickfixed; + Image offscreen; + Dimension offscreensize; + Graphics offgraphics; + static final Color fixedColor = Color.red; + static final Color selectColor = Color.pink; + final Color nodeColor = new Color(250, 220, 100); + final Font default_font=new Font("Helvetica",Font.PLAIN,12); + Log log=LogFactory.getLog(getClass()); + + + + private Frame findParent() { + Component retval=getParent(); + + while(retval != null) { + if(retval instanceof Frame) + return (Frame)retval; + retval=retval.getParent(); + } + return null; + } + + + + Node findNodeAtPoint(Point p) { + int x=p.x, y=p.y; + Node n; + + synchronized(nodes) { + if(nodes.size() < 1) + return null; + for(int i=nodes.size()-1; i >= 0; i--) { + n=(Node)nodes.elementAt(i); + if(x >= n.xloc && x <= (n.xloc + n.width) && y >= n.yloc && y <= (n.yloc + n.height)) + return n; + } + } + return null; + } + + + + public GraphPanel(Whiteboard wb) { + this.wb = wb; + addMouseListener(this); + addMouseMotionListener(this); + } + + + public void addNode(String lbl, Address addr, int xloc, int yloc) { + Node n = new Node(); + n.x = xloc; + n.y = yloc; + n.lbl = lbl; + n.addr=addr; + nodes.addElement(n); + repaint(); + } + + + public void removeNode(Object addr) { + Node n; + Object a; + + if(addr == null) { + log.error("removeNode(): address of node to be removed is null !"); + return; + } + + synchronized(nodes) { + for(int i=0; i < nodes.size(); i++) { + n=(Node)nodes.elementAt(i); + a=n.addr; + if(a == null) + continue; + if(addr.equals(a)) { + nodes.removeElement(n); + System.out.println("Removed node " + n); + break; + } + } + repaint(); + } + } + + + // Removes nodes that are not in the view + public void adjustNodes(Vector v) { + Node n; + boolean removed=false; + + synchronized(nodes) { + for(int i=0; i < nodes.size(); i++) { + n=(Node)nodes.elementAt(i); + if(!v.contains(n.addr)) { + System.out.println("adjustNodes(): node " + n + " was removed"); + nodes.removeElement(n); + removed=true; + } + } + if(removed) + repaint(); + } + } + + + public void paintNode(Graphics g, Node n, FontMetrics fm) { + String addr=n.addr != null ? n.addr.toString() : null; + int x = (int)n.x; + int y = (int)n.y; + g.setColor((n == pick) ? selectColor : (n.fixed ? fixedColor : nodeColor)); + int w = fm.stringWidth(n.lbl) + 10; + + if(addr != null) + w=Math.max(w, fm.stringWidth(addr) + 10); + + if(addr == null) + addr=""; + + int h = (fm.getHeight() + 4) * 2; + n.width=w; + n.height=h; + n.xloc=x - w/2; + n.yloc=y - h/2; + g.fillRect(x - w/2, y - h / 2, w, h); + g.setColor(Color.black); + g.drawRect(x - w/2, y - h / 2, w-1, h-1); + g.drawString(n.lbl, x - (w-10)/2, (y - (h-4)/2) + fm.getAscent()); + g.drawString(addr, x - (w-10)/2, (y - (h-4)/2) + 2 * fm.getAscent() +4 ); + } + + + + public synchronized void update(Graphics g) { + Dimension d = getSize(); + if ((offscreen == null) || (d.width != offscreensize.width) || + (d.height != offscreensize.height)) { + offscreen = createImage(d.width, d.height); + offscreensize = d; + offgraphics = offscreen.getGraphics(); + offgraphics.setFont(default_font); + } + + offgraphics.setColor(getBackground()); + offgraphics.fillRect(0, 0, d.width, d.height); + + FontMetrics fm = offgraphics.getFontMetrics(); + for (int i = 0; i < nodes.size(); i++) { + paintNode(offgraphics, (Node)nodes.elementAt(i), fm); + } + + g.drawImage(offscreen, 0, 0, null); + } + + + + + + public void mouseDragged(MouseEvent e) { + Point p=e.getPoint(); + int mod=e.getModifiers(); + + if(pick == null) + return; + + pick.x=p.x; + pick.y=p.y; + repaint(); + } + + + + + public void mousePressed(MouseEvent e) { + Point p=e.getPoint(); + double bestdist = Double.MAX_VALUE, dist; + int mod=e.getModifiers(); + Node n; + String msg; + + + if((mod & MouseEvent.BUTTON3_MASK) != 0) { + System.out.println("\nright button at " + p); + n=findNodeAtPoint(p); + if(n != null) { + System.out.println("Found node at " + p + ": " + n); + SendDialog dlg=new SendDialog(findParent(), n, myname, wb.disp); + repaint(); + } + e.consume(); + return; + } + + + for (int i = 0 ; i < nodes.size() ; i++) { + n=(Node)nodes.elementAt(i); + dist = (n.x - p.x) * (n.x - p.x) + (n.y - p.y) * (n.y - p.y); + if (dist < bestdist) { + pick = n; + bestdist = dist; + } + } + pickfixed = pick.fixed; + pick.fixed = true; + pick.x = p.x; + pick.y = p.y; + repaint(); + } + + + + + public void mouseReleased(MouseEvent e) { + Point p=e.getPoint(); + int mod=e.getModifiers(); + + if(pick == null) + return; + + pick.x = p.x; + pick.y = p.y; + pick.fixed = pickfixed; + + + try { + MethodCall call = new MethodCall("moveNode", new Object[] {pick}, new String[] {Node.class.getName()}); + wb.disp.callRemoteMethods(null, call, GroupRequest.GET_ALL, 0); + } + catch(Exception ex) { + log.error(ex); + } + + pick = null; + } + + + public void mouseEntered(MouseEvent e) {} + public void mouseExited(MouseEvent e) {} + public void mouseMoved(MouseEvent e) {} + public void mouseClicked(MouseEvent e) {} + + + + + public void start(String name) { + myname=name; + int xloc = (int)(10 + 250*Math.random()); + int yloc = (int)(10 + 250*Math.random()); + + try { + MethodCall call=new MethodCall("addNode", + new Object[] {name, my_addr, new Integer(xloc), new Integer(yloc)}, + new String[] {String.class.getName(), Address.class.getName(), int.class.getName(), int.class.getName()}); + wb.disp.callRemoteMethods(null, call, GroupRequest.GET_ALL, 0); + } + catch(Exception e) { + log.error(e); + } + repaint(); + } + + + public void stop() { + nodes.removeAllElements(); + } + + + + + public void saveState() { + copy.removeAllElements(); + synchronized(nodes) { + for(int i=0; i < nodes.size(); i++) + copy.addElement(nodes.elementAt(i)); + } + } + + + public byte[] getState() { // return the copy previously saved by saveState() + try { + return Util.objectToByteBuffer(copy); + } + catch(Throwable ex) { + ex.printStackTrace(); + return null; + } + } + + + public void setState(byte[] data) { + Vector n; + Object new_state; + + try { + new_state=Util.objectFromByteBuffer(data); + } + catch(Exception ex) { + ex.printStackTrace(); + return; + } + + synchronized(nodes) { + nodes.removeAllElements(); + if(new_state != null) { + n=(Vector)new_state; + for(int i=0; i < n.size(); i++) + nodes.addElement(n.elementAt(i)); + repaint(); + } + } + } + + + public void moveNode(Node n) { + Node tmp; + boolean changed=false; + + synchronized(nodes) { + for(int i=0; i < nodes.size(); i++) { + tmp=(Node)nodes.elementAt(i); + if(n.addr.equals(tmp.addr)) { + tmp.x=n.x; + tmp.y=n.y; + changed=true; + break; + } + } + if(changed) + repaint(); + } + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/demos/wb/MessageDialog.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/demos/wb/MessageDialog.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/demos/wb/MessageDialog.java 17 Aug 2012 14:51:03 -0000 1.1 @@ -0,0 +1,51 @@ +// $Id: MessageDialog.java,v 1.1 2012/08/17 14:51:03 marcin Exp $ + +package org.jgroups.demos.wb; + + +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + + + +public class MessageDialog extends Dialog implements ActionListener { + private final TextArea text=new TextArea(""); + private final Font default_font=new Font("Helvetica",Font.PLAIN,12); + + + public MessageDialog(Frame parent, String sender, String msg) { + super(parent, "Msg from " + sender); + + Button ok=new Button("OK"); + + setLayout(new BorderLayout()); + setBackground(Color.white); + + ok.setFont(default_font); + text.setFont(default_font); + text.setEditable(false); + text.setText(msg); + + ok.addActionListener(this); + + add("Center", text); + add("South", ok); + + setSize(300, 150); + + Point my_loc=parent.getLocation(); + my_loc.x+=50; + my_loc.y+=150; + setLocation(my_loc); + Toolkit.getDefaultToolkit().beep(); + show(); + } + + + public void actionPerformed(ActionEvent e) { + dispose(); + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/demos/wb/Node.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/demos/wb/Node.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/demos/wb/Node.java 17 Aug 2012 14:51:03 -0000 1.1 @@ -0,0 +1,24 @@ +// $Id: Node.java,v 1.1 2012/08/17 14:51:03 marcin Exp $ + +package org.jgroups.demos.wb; +import org.jgroups.Address; + + + +public class Node implements java.io.Serializable { + public double x, y, dx, dy; + public boolean fixed; + public String lbl=null; + public Address addr=null; + public int xloc=0, yloc=0; + public int width=0; + public int height=0; + + + public String toString() { + StringBuilder ret=new StringBuilder(); + ret.append("name=" + lbl + ", addr=" + addr + " at " + x + ',' + y); + return ret.toString(); + } +} + Index: 3rdParty_sources/jgroups/org/jgroups/demos/wb/SendDialog.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/demos/wb/SendDialog.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/demos/wb/SendDialog.java 17 Aug 2012 14:51:03 -0000 1.1 @@ -0,0 +1,94 @@ +// $Id: SendDialog.java,v 1.1 2012/08/17 14:51:03 marcin Exp $ + +package org.jgroups.demos.wb; + +import org.jgroups.blocks.GroupRequest; +import org.jgroups.blocks.MethodCall; +import org.jgroups.blocks.RpcDispatcher; + +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + + + +public class SendDialog extends Dialog implements ActionListener { + private final TextArea msg=new TextArea(""); + private final Font default_font=new Font("Helvetica",Font.PLAIN,12); + private RpcDispatcher disp=null; + private Node dest=null; + private String sender=null; + + + + public SendDialog(Frame parent, Node dest, String src, RpcDispatcher disp) { + super(parent, "Send message to " + dest.lbl + " at " + dest.addr, true); + + Panel p1=new Panel(), p2=new Panel(); + Button send=new Button("Send"), send_all=new Button("Send to all"); + Button cancel=new Button("Cancel"); + + this.disp=disp; + this.dest=dest; + sender=src; + + send.setFont(default_font); + send_all.setFont(default_font); + cancel.setFont(default_font); + msg.setFont(default_font); + + p1.setLayout(new BorderLayout()); + p1.add(msg); + + p2.setLayout(new FlowLayout()); + send.addActionListener(this); + send_all.addActionListener(this); + cancel.addActionListener(this); + p2.add(send); p2.add(send_all); p2.add(cancel); + + add("Center", p1); + add("South", p2); + + setSize(300, 150); + + Point my_loc=parent.getLocation(); + my_loc.x+=50; + my_loc.y+=150; + setLocation(my_loc); + show(); + } + + + + public String getMessage() { + String retval=msg.getText(); + return retval.length() > 0 ? retval : null; + } + + + public void actionPerformed(ActionEvent e) { + String command=e.getActionCommand(); + String retval=msg.getText(); + + if(retval == null || retval.length() < 1) { + dispose(); + return; + } + + try { + MethodCall call = new MethodCall("displayMessage", new Object[] {sender, retval}, + new String[] {String.class.getName(), String.class.getName()}); + if(command.equals("Send")) + disp.callRemoteMethod(dest.addr, call, GroupRequest.GET_FIRST, 0); + else if(command.equals("Send to all")) + disp.callRemoteMethods(null, call, GroupRequest.GET_ALL, 0); + } + catch(Throwable ex) { + System.err.println(ex); + } + + dispose(); + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/demos/wb/UserInfoDialog.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/demos/wb/UserInfoDialog.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/demos/wb/UserInfoDialog.java 17 Aug 2012 14:51:03 -0000 1.1 @@ -0,0 +1,70 @@ +// $Id: UserInfoDialog.java,v 1.1 2012/08/17 14:51:03 marcin Exp $ + +package org.jgroups.demos.wb; + +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + + + +public class UserInfoDialog extends Dialog implements ActionListener { + + final Button ok=new Button("OK"); + final Label l=new Label("Name: "); + final TextField name=new TextField(""); + private final Font default_font=new Font("Helvetica",Font.PLAIN,12); + + + public UserInfoDialog(Frame parent) { + super(parent, "Input", true); + setLayout(null); + + l.setFont(default_font); + l.setSize(50, 30); + l.setLocation(30, 50); + + name.setFont(default_font); + name.setSize(150, 30); + name.setLocation(90, 50); + //name.selectAll(); + + ok.setFont(default_font); + ok.setSize(50, 30); + ok.setLocation(30, 90); + + + add(l); add(name); add(ok); + ok.addActionListener(this); + setSize(300, 150); + + Point my_loc=parent.getLocation(); + my_loc.x+=50; + my_loc.y+=150; + setLocation(my_loc); + show(); + } + + + public String getUserName() { + return name.getText(); + } + + + public void actionPerformed(ActionEvent e) { + String command=e.getActionCommand(); + String tmp=name.getText(); + + if(command == "OK") { + if(tmp == null || tmp.length() < 1) + return; + else + dispose(); + } + else + System.err.println("UserInfoDialog.actionPerfomed(): unknown action " + + e.getActionCommand()); + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/demos/wb/Whiteboard.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/demos/wb/Whiteboard.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/demos/wb/Whiteboard.java 17 Aug 2012 14:51:03 -0000 1.1 @@ -0,0 +1,313 @@ +// $Id: Whiteboard.java,v 1.1 2012/08/17 14:51:03 marcin Exp $ + +package org.jgroups.demos.wb; + +import org.jgroups.*; +import org.jgroups.blocks.*; +import org.jgroups.blocks.GroupRequest; +import org.jgroups.blocks.RpcDispatcher; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.applet.Applet; +import java.awt.*; +import java.awt.event.*; + + + +/** + * Shared whiteboard: members are represented by rectangles that contain their names and the OS/arch of + * the machine they are working on. The boxes can be moved by anyone and by clicking on them, messages can + * be sent to specific or all members. Whiteboard is both an application and an applet. + * @author Bela Ban + */ +public class Whiteboard extends Applet implements ActionListener, MessageListener, MembershipListener, + ComponentListener, FocusListener { + public RpcDispatcher disp; + Channel channel; + GraphPanel panel; + private Button leave_button; + private Label mbr_label; + private final Font default_font=new Font("Helvetica",Font.PLAIN,12); + private String props=null; + public static final String groupname="WbGrp"; + private boolean application=false; + Log log=LogFactory.getLog(getClass()); + + + public void receive(Message m) { + ; + } + + public byte[] getState() { + panel.saveState(); + return panel.getState(); + } + + public void setState(byte[] new_state) { + panel.setState(new_state); + } + + + private String getInfo() { + StringBuilder ret = new StringBuilder(); + ret.append(" (" + System.getProperty("os.name") + ' ' + System.getProperty("os.version") + + ' ' + System.getProperty("os.arch") + ')'); + return ret.toString(); + } + + private Frame findParent() { + Component retval = getParent(); + + while (retval != null) { + if (retval instanceof Frame) + return (Frame) retval; + retval = retval.getParent(); + } + return null; + } + + + public Whiteboard() { // called when started as applet + } + + public Whiteboard(String properties) { // called when started as application + application = true; + props = properties; + + } + + + public void init() { + setLayout(new BorderLayout()); + panel = new GraphPanel(this); + panel.setBackground(Color.white); + add("Center", panel); + Panel p = new Panel(); + leave_button = new Button("Exit"); + leave_button.setFont(default_font); + leave_button.addActionListener(this); + mbr_label = new Label("1 mbr(s)"); + mbr_label.setFont(default_font); + p.add("South", leave_button); + p.add("South", mbr_label); + add("South", p); + + if (!application) + props = getParameter("properties"); + if (props == null) { + props = "udp.xml"; + } + + System.out.println("properties are " + props); + + try { + channel = new JChannel(props); + disp = new RpcDispatcher(channel, this, this, this); + channel.connect(groupname); + channel.getState(null, 0); + } catch (Exception e) { + log.error("Whiteboard.init(): " + e); + } + panel.my_addr = channel.getLocalAddress(); + + + UserInfoDialog dlg = new UserInfoDialog(findParent()); + String n = dlg.getUserName(); + String info = getInfo(); + panel.start(n + info); + + + addComponentListener(this); + addFocusListener(this); + } + + + public void destroy() { + if (disp != null) { + try { + MethodCall call = new MethodCall("removeNode", new Object[] {panel.my_addr}, new String[] {Object.class.getName()}); + disp.callRemoteMethods(null, call, GroupRequest.GET_ALL, 0); + } catch (Exception e) { + log.error(e); + } + channel.close(); + disp = null; + if (panel != null) { + panel.stop(); + panel = null; + } + } + + } + + + public void repaint() { + if (panel != null) + panel.repaint(); + } + + + public void actionPerformed(ActionEvent e) { + String command = e.getActionCommand(); + + if ("Exit".equals(command)) { + try { + setVisible(false); + destroy(); + if (application) { + ((Frame) getParent()).dispose(); + System.exit(0); + } + } catch (Exception ex) { + log.error(ex); + } + + } else + System.out.println("Unknown action"); + } + + + public void viewAccepted(View v) { + if (v != null) { + if (mbr_label != null) + mbr_label.setText(v.size() + " mbr(s)"); + } + panel.adjustNodes(v.getMembers()); + } + + public void suspect(Address obj) { + } + + public void block() { + } + + + public void moveNode(Node n) { + panel.moveNode(n); + } + + + public void addNode(String lbl, Address addr, int xloc, int yloc) { + panel.addNode(lbl, addr, xloc, yloc); + } + + + public void removeNode(Object addr) { + panel.removeNode(addr); + } + + + public void displayMessage(String sender, String msg) { + new MessageDialog(findParent(), sender, msg); + panel.repaint(); + } + + + public void componentResized(ComponentEvent e) { + if (panel != null) panel.repaint(); + } + + public void componentMoved(ComponentEvent e) { + } + + public void componentShown(ComponentEvent e) { + if (panel != null) panel.repaint(); + } + + public void componentHidden(ComponentEvent e) { + } + + + public void focusGained(FocusEvent e) { + if (panel != null) panel.repaint(); + } + + + public void focusLost(FocusEvent e) { + } + + + public static void main(String[] args) { + String props = null; + + for (int i = 0; i < args.length; i++) { + if ("-props".equals(args[i])) { + props = args[++i]; + continue; + } + help(); + return; + } + + Whiteboard wb = new Whiteboard(props); + new ApplFrame("Whiteboard Application", wb); + } + + static void help() { + System.out.println("Whiteboard [-help] [-props ]"); + } + + +} + + +class ApplFrame extends Frame implements WindowListener, ComponentListener { + Whiteboard wb = null; + + public ApplFrame(String title, Whiteboard wb) { + super(title); + this.wb = wb; + add(wb); + setSize(299, 299); + setVisible(true); + wb.init(); + setSize(300, 300); + addWindowListener(this); + addComponentListener(this); + } + + + public void windowOpened(WindowEvent e) { + } + + public void windowClosing(WindowEvent e) { + dispose(); + System.exit(0); + } + + public void windowClosed(WindowEvent e) { + } + + public void windowIconified(WindowEvent e) { + } + + public void windowDeiconified(WindowEvent e) { + wb.repaint(); + } + + public void windowActivated(WindowEvent e) { + wb.repaint(); + } + + public void windowDeactivated(WindowEvent e) { + } + + public void componentResized(ComponentEvent e) { + wb.repaint(); + } + + public void componentMoved(ComponentEvent e) { + } + + + public void componentShown(ComponentEvent e) { + } + + public void componentHidden(ComponentEvent e) { + } + + +} + + Index: 3rdParty_sources/jgroups/org/jgroups/demos/wb/package.html =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/demos/wb/package.html,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/demos/wb/package.html 17 Aug 2012 14:51:03 -0000 1.1 @@ -0,0 +1,5 @@ + + + A distributed whiteboard applet implemented using JGroups. + + \ No newline at end of file Index: 3rdParty_sources/jgroups/org/jgroups/demos/wb/wb.html =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/demos/wb/wb.html,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/demos/wb/wb.html 17 Aug 2012 14:51:03 -0000 1.1 @@ -0,0 +1,25 @@ + + + + + A Simple Whiteboard Program + + + +
+ + + + + + + + + + +/HTML> + + + + Index: 3rdParty_sources/jgroups/org/jgroups/jmx/JChannel.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/JChannel.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/JChannel.java 17 Aug 2012 14:51:26 -0000 1.1 @@ -0,0 +1,402 @@ +package org.jgroups.jmx; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.*; +import org.w3c.dom.Attr; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.NodeList; + +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import java.io.Serializable; +import java.util.Map; + +/** + * @author Bela Ban + * @version $Id: JChannel.java,v 1.1 2012/08/17 14:51:26 marcin Exp $ + */ +public class JChannel implements JChannelMBean { + /** Ref to the original JGroups channel */ + org.jgroups.JChannel channel; + String props=null; + String group_name="TestGroup"; + String object_name=null; + Log log=LogFactory.getLog(getClass()); + + /*flag to indicate whether to receive blocks, if this is set to true, receive_views is set to true*/ + private boolean receive_blocks=false; + /*flag to indicate whether to receive local messages + *if this is set to false, the JChannel will not receive messages sent by itself*/ + private boolean receive_local_msgs=true; + /*flag to indicate whether the channel will reconnect (reopen) when the exit message is received*/ + private boolean auto_reconnect=false; + /*flag t indicate whether the state is supposed to be retrieved after the channel is reconnected + *setting this to true, automatically forces auto_reconnect to true*/ + private boolean auto_getstate=false; + + private String mbean_server_name=null; + + + public JChannel() { + } + + public JChannel(org.jgroups.JChannel channel) { + this.channel=channel; + setValues(); + } + + + protected final void setValues() { + if(channel != null) { + props=getProperties(); + group_name=channel.getClusterName(); + receive_blocks=getReceiveBlockEvents(); + receive_local_msgs=getReceiveLocalMessages(); + auto_reconnect=getAutoReconnect(); + auto_getstate=getAutoGetState(); + } + } + + //void jbossInternalLifecycle(String method) throws Exception; + public org.jgroups.JChannel getChannel() { + return channel; + } + + public String getVersion() { + return Version.printDescription(); + } + + public String getMBeanServerName() { + return mbean_server_name; + } + + public void setMBeanServerName(String n) { + this.mbean_server_name=n; + } + + public String getProperties() { + props=channel.getProperties(); + return props; + } + + public void setProperties(String props) { + this.props=props; + } + + public String getObjectName() { + return object_name; + } + + public void setObjectName(String name) { + object_name=name; + } + + public int getNumberOfTasksInTimer() { + return channel.getNumberOfTasksInTimer(); + } + + public String dumpTimerQueue() { + return channel.dumpTimerQueue(); + } + + public void setClusterConfig(Element config) { + StringBuilder buffer=new StringBuilder(); + NodeList stack=config.getChildNodes(); + int length=stack.getLength(); + + for(int s=0; s < length; s++) { + org.w3c.dom.Node node=stack.item(s); + if(node.getNodeType() != org.w3c.dom.Node.ELEMENT_NODE) + continue; + + Element tag=(Element)node; + String protocol=tag.getTagName(); + buffer.append(protocol); + NamedNodeMap attrs=tag.getAttributes(); + int attrLength=attrs.getLength(); + if(attrLength > 0) + buffer.append('('); + for(int a=0; a < attrLength; a++) { + Attr attr=(Attr)attrs.item(a); + String name=attr.getName(); + String value=attr.getValue(); + buffer.append(name); + buffer.append('='); + buffer.append(value); + if(a < attrLength - 1) + buffer.append(';'); + } + if(attrLength > 0) + buffer.append(')'); + buffer.append(':'); + } + // Remove the trailing ':' + buffer.setLength(buffer.length() - 1); + setProperties(buffer.toString()); + if(log.isDebugEnabled()) + log.debug("setting cluster properties from xml to: " + props); + } + + public String getGroupName() { + if(channel != null) + group_name=channel.getClusterName(); + return group_name; + } + + public void setGroupName(String group_name) { + this.group_name=group_name; + } + + public String getClusterName() { + return getGroupName(); + } + + public void setClusterName(String cluster_name) { + setGroupName(cluster_name); + } + + public boolean getReceiveBlockEvents() { + if(channel != null) + receive_blocks=((Boolean)channel.getOpt(Channel.BLOCK)).booleanValue(); + return receive_blocks; + } + + public void setReceiveBlockEvents(boolean flag) { + this.receive_blocks=flag; + if(channel != null) + channel.setOpt(Channel.BLOCK, Boolean.valueOf(flag)); + } + + public boolean getReceiveLocalMessages() { + if(channel != null) + receive_local_msgs=((Boolean)channel.getOpt(Channel.LOCAL)).booleanValue(); + return receive_local_msgs; + } + + public void setReceiveLocalMessages(boolean flag) { + this.receive_local_msgs=flag; + if(channel != null) + channel.setOpt(Channel.LOCAL, Boolean.valueOf(flag)); + } + + public boolean getAutoReconnect() { + if(channel != null) + auto_reconnect=((Boolean)channel.getOpt(Channel.AUTO_RECONNECT)).booleanValue(); + return auto_reconnect; + } + + public void setAutoReconnect(boolean flag) { + this.auto_reconnect=flag; + if(channel != null) { + channel.setOpt(Channel.AUTO_RECONNECT, Boolean.valueOf(flag)); + } + } + + public boolean getAutoGetState() { + if(channel != null) + auto_getstate=((Boolean)channel.getOpt(Channel.AUTO_GETSTATE)).booleanValue(); + return auto_getstate; + } + + public void setAutoGetState(boolean flag) { + this.auto_getstate=flag; + if(channel != null) + channel.setOpt(Channel.AUTO_GETSTATE, Boolean.valueOf(flag)); + } + + public boolean getStatsEnabled() { + return channel.statsEnabled(); + } + + public void setStatsEnabled(boolean flag) { + channel.enableStats(flag); + } + + public Map dumpStats() { + return channel.dumpStats(); + } + + public void resetStats() { + channel.resetStats(); + } + + public long getSentMessages() {return channel.getSentMessages();} + public long getSentBytes() {return channel.getSentBytes();} + public long getReceivedMessages() {return channel.getReceivedMessages();} + public long getReceivedBytes() {return channel.getReceivedBytes();} + + + public int getTimerThreads() { + return channel.getTimerThreads(); + } + + public void create() throws Exception { + if(channel != null) + channel.close(); + channel=new org.jgroups.JChannel(props); + setOptions(); + setValues(); + MBeanServer server=(MBeanServer)MBeanServerFactory.findMBeanServer(mbean_server_name).get(0); + JmxConfigurator.registerProtocols(server, channel, getObjectName()); + } + + public void start() throws Exception { + channel.connect(group_name); + } + + public void stop() { + if(channel != null) + channel.disconnect(); + } + + public void destroy() { + MBeanServer server=(MBeanServer)MBeanServerFactory.findMBeanServer(mbean_server_name).get(0); + JmxConfigurator.unregisterProtocols(server, channel, getObjectName()); + if(channel != null) { + channel.close(); + channel=null; + } + } + +// public void jbossInternalLifecycle(String method) throws Exception { +// System.out.println("method: " + method); +// if (method == null) +// throw new IllegalArgumentException("Null method name"); +// +// if (method.equals("create")) +// create(); +// else if (method.equals("start")) +// start(); +// else if (method.equals("stop")) +// stop(); +// else if (method.equals("destroy")) +// destroy(); +// else +// throw new IllegalArgumentException("Unknown lifecyle method " + method); +// } + + + public View getView() { + return channel.getView(); + } + + public String getViewAsString() { + View v=channel.getView(); + return v != null ? v.toString() : "n/a"; + } + + public Address getLocalAddress() { + return channel.getLocalAddress(); + } + + public String getLocalAddressAsString() { + Address addr=getLocalAddress(); + return addr != null? addr.toString() : "n/a"; + } + + /** @deprecated Use addChannelListener() instead */ + public void setChannelListener(ChannelListener channel_listener) { + if(channel != null) + channel.addChannelListener(channel_listener); + } + + public void addChannelListener(ChannelListener listener) { + if(channel != null) + channel.addChannelListener(listener); + } + + public void removeChannelListener(ChannelListener l) { + if(channel != null) + channel.removeChannelListener(l); + } + + public boolean isOpen() { + return channel.isOpen(); + } + + public boolean isConnected() { + return channel.isConnected(); + } + + public int getNumMessages() { + return channel.getNumMessages(); + } + + public String dumpQueue() { + return channel.dumpQueue(); + } + + public String printProtocolSpec(boolean include_properties) { + return channel.printProtocolSpec(include_properties); + } + + public String toString(boolean print_details) { + return channel.toString(print_details); + } + + public void connect(String channel_name) throws ChannelException { + channel.connect(channel_name); + } + + public void disconnect() { + channel.disconnect(); + } + + public void close() { + channel.close(); + } + + public void shutdown() { + channel.shutdown(); + } + + public void send(Message msg) throws ChannelNotConnectedException, ChannelClosedException { + channel.send(msg); + } + + public void send(Address dst, Address src, Serializable obj) throws ChannelNotConnectedException, ChannelClosedException { + channel.send(dst, src, obj); + } + + public void sendToAll(String msg) throws ChannelNotConnectedException, ChannelClosedException { + send(null, null, msg); + } + + public void down(Event evt) { + channel.down(evt); + } + + public Object receive(long timeout) throws ChannelNotConnectedException, ChannelClosedException, TimeoutException { + return channel.receive(timeout); + } + + public Object peek(long timeout) throws ChannelNotConnectedException, ChannelClosedException, TimeoutException { + return channel.peek(timeout); + } + + public void blockOk() { + channel.blockOk(); + } + + public boolean getState(Address target, long timeout) throws ChannelNotConnectedException, ChannelClosedException { + return channel.getState(target, timeout); + } + + public void returnState(byte[] state) { + channel.returnState(state); + } + + public void returnState(byte[] state, String state_id) { + channel.returnState(state, state_id); + } + + private void setOptions() { + channel.setOpt(Channel.BLOCK, Boolean.valueOf(this.receive_blocks)); + channel.setOpt(Channel.LOCAL, Boolean.valueOf(this.receive_local_msgs)); + channel.setOpt(Channel.AUTO_RECONNECT, Boolean.valueOf(this.auto_reconnect)); + channel.setOpt(Channel.AUTO_GETSTATE, Boolean.valueOf(this.auto_getstate)); + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/JChannelFactory.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/JChannelFactory.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/JChannelFactory.java 17 Aug 2012 14:51:26 -0000 1.1 @@ -0,0 +1,131 @@ +package org.jgroups.jmx; + +import org.jgroups.Channel; + +import javax.management.MBeanRegistration; +import javax.management.ObjectName; +import javax.management.MBeanServer; + +/** + * @author Bela Ban + * @version $Id: JChannelFactory.java,v 1.1 2012/08/17 14:51:26 marcin Exp $ + */ +public class JChannelFactory implements JChannelFactoryMBean, MBeanRegistration { + org.jgroups.JChannelFactory factory=new org.jgroups.JChannelFactory(); + private MBeanServer server=null; + + + public JChannelFactory(org.jgroups.JChannelFactory factory) { + this.factory=factory; + } + + public JChannelFactory() { + } + + public void setMultiplexerConfig(String properties) throws Exception { + factory.setMultiplexerConfig(properties); + } + + + public void setMultiplexerConfig(String properties, boolean replace) throws Exception { + factory.setMultiplexerConfig(properties, replace); + } + + public String getMultiplexerConfig() { + return factory.getMultiplexerConfig(); + } + + public String getConfig(String stack_name) throws Exception { + return factory.getConfig(stack_name); + } + + public boolean removeConfig(String stack_name) { + return factory.removeConfig(stack_name); + } + + public void clearConfigurations() { + factory.clearConfigurations(); + } + + public String getDomain() { + return factory.getDomain(); + } + + public void setDomain(String name) { + factory.setDomain(name); + } + + public boolean isExposeChannels() { + return factory.isExposeChannels(); + } + + public void setExposeChannels(boolean flag) { + factory.setExposeChannels(flag); + } + + public boolean isExposeProtocols() { + return factory.isExposeProtocols(); + } + + public void setExposeProtocols(boolean f) { + factory.setExposeProtocols(f); + } + + + public Channel createMultiplexerChannel(String stack_name, String id) throws Exception { + return factory.createMultiplexerChannel(stack_name, id); + } + + public Channel createMultiplexerChannel(String stack_name, String id, boolean register_for_state_transfer, String substate_id) throws Exception { + return factory.createMultiplexerChannel(stack_name, id, register_for_state_transfer, substate_id); + } + + public void create() throws Exception { + if(factory == null) + factory=new org.jgroups.JChannelFactory(); + factory.setServer(server); + factory.create(); + } + + public void start() throws Exception { + factory.start(); + } + + public void stop() { + factory.stop(); + } + + public void destroy() { + factory.destroy(); + factory=null; + } + + public String dumpConfiguration() { + return factory.dumpConfiguration(); + } + + public String dumpChannels() { + return factory.dumpChannels(); + } + + public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception { + this.server=server; + if(factory != null) + factory.setServer(server); + if (name != null) {//we only create a name if it is empty + return name; + } + //if the objectName is not a pattern then at least one key property MUST exist + String objectName = getDomain() + " : service=JChannelFactory"; + return ObjectName.getInstance(objectName); + } + + public void postRegister(Boolean registrationDone) { + } + + public void preDeregister() throws Exception { + } + + public void postDeregister() { + } +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/JChannelFactoryMBean.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/JChannelFactoryMBean.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/JChannelFactoryMBean.java 17 Aug 2012 14:51:26 -0000 1.1 @@ -0,0 +1,36 @@ +package org.jgroups.jmx; + +import org.jgroups.Channel; + +/** + * @author Bela Ban + * @version $Id: JChannelFactoryMBean.java,v 1.1 2012/08/17 14:51:26 marcin Exp $ + */ +public interface JChannelFactoryMBean { + String getMultiplexerConfig(); + void setMultiplexerConfig(String properties) throws Exception; + void setMultiplexerConfig(String properties, boolean replace) throws Exception; + + String getDomain(); + void setDomain(String name); + + boolean isExposeChannels(); + void setExposeChannels(boolean flag); + + boolean isExposeProtocols(); + void setExposeProtocols(boolean f); + + String getConfig(String stack_name) throws Exception; + boolean removeConfig(String stack_name); + void clearConfigurations(); + + + Channel createMultiplexerChannel(String stack_name, String id) throws Exception; + Channel createMultiplexerChannel(String stack_name, String id, boolean register_for_state_transfer, String substate_id) throws Exception; + void create() throws Exception; + void start() throws Exception; + void stop(); + void destroy(); + String dumpConfiguration(); + String dumpChannels(); +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/JChannelMBean.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/JChannelMBean.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/JChannelMBean.java 17 Aug 2012 14:51:26 -0000 1.1 @@ -0,0 +1,111 @@ +package org.jgroups.jmx; + +import org.jgroups.*; +import org.w3c.dom.Element; + +import java.io.Serializable; +import java.util.Map; + +/** + * @author Bela Ban + * @version $Id: JChannelMBean.java,v 1.1 2012/08/17 14:51:26 marcin Exp $ + */ +public interface JChannelMBean { + void create() throws Exception; + void start() throws Exception; + void stop(); + void destroy(); + + //void jbossInternalLifecycle(String method) throws Exception; + org.jgroups.JChannel getChannel(); + + String getProperties(); + void setProperties(String props); + + String getVersion(); + + String getObjectName(); + void setObjectName(String name); + + int getNumberOfTasksInTimer(); + String dumpTimerQueue(); + int getTimerThreads(); + + /** To configure via XML file */ + void setClusterConfig(Element el); + + String getGroupName(); + void setGroupName(String group_name); + + String getClusterName(); + void setClusterName(String cluster_name); + + boolean getReceiveBlockEvents(); + void setReceiveBlockEvents(boolean flag); + + boolean getReceiveLocalMessages(); + void setReceiveLocalMessages(boolean flag); + + boolean getAutoReconnect(); + void setAutoReconnect(boolean flag); + + boolean getAutoGetState(); + void setAutoGetState(boolean flag); + + Map dumpStats(); + + View getView(); + String getViewAsString(); + Address getLocalAddress(); + String getLocalAddressAsString(); + void setChannelListener(ChannelListener channel_listener); + boolean getStatsEnabled(); + void setStatsEnabled(boolean flag); + void resetStats(); + long getSentMessages(); + long getSentBytes(); + long getReceivedMessages(); + long getReceivedBytes(); + + boolean isOpen(); + + boolean isConnected(); + + int getNumMessages(); + + String dumpQueue(); + + String printProtocolSpec(boolean include_properties); + + String toString(boolean print_details); + + void connect(String channel_name) throws ChannelException, ChannelClosedException; + + void disconnect(); + + void close(); + + void shutdown(); + + void send(Message msg) throws ChannelNotConnectedException, ChannelClosedException; + + void send(Address dst, Address src, Serializable obj) throws ChannelNotConnectedException, ChannelClosedException; + + void sendToAll(String msg) throws ChannelNotConnectedException, ChannelClosedException; + + /** @param evt + * @deprecated */ + void down(Event evt); + + Object receive(long timeout) throws ChannelNotConnectedException, ChannelClosedException, TimeoutException; + + Object peek(long timeout) throws ChannelNotConnectedException, ChannelClosedException, TimeoutException; + + void blockOk(); + + boolean getState(Address target, long timeout) throws ChannelNotConnectedException, ChannelClosedException; + + void returnState(byte[] state); + + void returnState(byte[] state, String state_id); +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/JmxConfigurator.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/JmxConfigurator.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/JmxConfigurator.java 17 Aug 2012 14:51:26 -0000 1.1 @@ -0,0 +1,157 @@ +package org.jgroups.jmx; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.stack.ProtocolStack; +import org.jgroups.util.Util; + +import javax.management.*; +import java.util.Vector; +import java.util.Set; +import java.util.Iterator; + +/** + * @author Bela Ban + * @version $Id: JmxConfigurator.java,v 1.1 2012/08/17 14:51:26 marcin Exp $ + */ +public class JmxConfigurator { + static final Log log=LogFactory.getLog(JmxConfigurator.class); + + /** + * Registers an already created channel with the MBeanServer. Creates an org.jgroups.jmx.JChannel which + * delegates to the org.jgroups.JChannel and registers it. Optionally, this method will also try to + * create one MBean proxy for each protocol in the channel's protocol stack, and register it as well. + * @param channel + * @param server + * @param domain Has to be a JMX ObjectName of the domain, e.g. DefaultDomain:name=JGroups + * @param register_protocols + * @return org.jgroups.jmx.JChannel for the specified org.jgroups.JChannel + */ + public static org.jgroups.jmx.JChannel registerChannel(org.jgroups.JChannel channel, + MBeanServer server, String domain, String cluster_name, + boolean register_protocols) throws Exception { + if(cluster_name == null) + cluster_name=channel != null? channel.getClusterName() : null; + if(cluster_name == null) + cluster_name="null"; + if(register_protocols) { + String tmp=domain + ":type=protocol,cluster=" +cluster_name; + registerProtocols(server, channel, tmp); + } + return registerChannel(channel, server, domain + ":type=channel,cluster=" +cluster_name); + } + + /** + * Registers an already created channel with the MBeanServer. Creates an org.jgroups.jmx.JChannel which + * delegates to the org.jgroups.JChannel and registers it. + * @param channel + * @param server + * @param name The JMX ObjectName + * @return org.jgroups.jmx.JChannel for the specified org.jgroups.JChannel + */ + public static org.jgroups.jmx.JChannel registerChannel(org.jgroups.JChannel channel, + MBeanServer server, String name) throws Exception { + JChannel retval=new JChannel(channel); + server.registerMBean(retval, new ObjectName(name)); + return retval; + } + + + + public static void unregisterChannel(MBeanServer server, ObjectName name) throws Exception { + if(server != null) + server.unregisterMBean(name); + } + + public static void unregisterChannel(MBeanServer server, String name) throws Exception { + if(server != null) + server.unregisterMBean(new ObjectName(name)); + } + + + public static org.jgroups.jmx.JChannelFactory registerChannelFactory(org.jgroups.JChannelFactory factory, + MBeanServer server, String name) throws Exception { + JChannelFactory retval=new JChannelFactory(factory); + server.registerMBean(retval, new ObjectName(name)); + return retval; + } + + + + + /** + * Takes all protocols of an existing stack, creates corresponding MBean proxies and registers them with + * the MBean server + * @param channel + * @param prefix + */ + public static void registerProtocols(MBeanServer server, org.jgroups.JChannel channel, String prefix) throws Exception { + ProtocolStack stack=channel.getProtocolStack(); + Vector protocols=stack.getProtocols(); + org.jgroups.stack.Protocol prot; + org.jgroups.jmx.Protocol p=null; + for(int i=0; i < protocols.size(); i++) { + prot=(org.jgroups.stack.Protocol)protocols.get(i); + try { + p=findProtocol(prot); + } + catch(ClassNotFoundException e) { + p=null; + } + catch(Throwable e) { + log.error("failed creating a JMX wrapper instance for " + prot, e); + p=null; + } + if(p == null) + p=new org.jgroups.jmx.Protocol(prot); + ObjectName prot_name=new ObjectName(prefix + ",protocol=" + prot.getName()); + server.registerMBean(p, prot_name); + } + } + + public static void unregisterProtocols(MBeanServer server, org.jgroups.JChannel channel, String channel_name) { + ProtocolStack stack=channel.getProtocolStack(); + Vector protocols=stack.getProtocols(); + org.jgroups.stack.Protocol prot; + ObjectName prot_name=null; + for(int i=0; i < protocols.size(); i++) { + prot=(org.jgroups.stack.Protocol)protocols.get(i); + try { + prot_name=new ObjectName(channel_name + ",protocol=" + prot.getName()); + server.unregisterMBean(prot_name); + } + catch(Throwable e) { + log.error("failed to unregister " + prot_name, e); + } + } + } + + /** + * Unregisters object_name and everything under it + * @param object_name + */ + public static void unregister(MBeanServer server, String object_name) throws Exception { + Set mbeans=server.queryNames(new ObjectName(object_name), null); + if(mbeans != null) { + ObjectName name; + for(Iterator it=mbeans.iterator(); it.hasNext();) { + name=(ObjectName)it.next(); + server.unregisterMBean(name); + } + } + } + + + protected static Protocol findProtocol(org.jgroups.stack.Protocol prot) throws ClassNotFoundException, IllegalAccessException, InstantiationException { + Protocol p; + String prot_name=prot.getClass().getName(); + String clname=prot_name.replaceFirst("org.jgroups.", "org.jgroups.jmx."); + Class cl=Util.loadClass(clname, JmxConfigurator.class); + if(cl != null) { + p=(Protocol)cl.newInstance(); + p.attachProtocol(prot); + return p; + } + return null; + } +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/Protocol.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/Protocol.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/Protocol.java 17 Aug 2012 14:51:26 -0000 1.1 @@ -0,0 +1,74 @@ +package org.jgroups.jmx; + + +import java.util.Properties; +import java.util.Map; + +/** + * @author Bela Ban + * @version $Id: Protocol.java,v 1.1 2012/08/17 14:51:26 marcin Exp $ + */ +public class Protocol implements ProtocolMBean { + org.jgroups.stack.Protocol prot; + + public Protocol() { + + } + + public Protocol(org.jgroups.stack.Protocol p) { + this.prot=p; + } + + public String getName() { + return prot.getName(); + } + + public void attachProtocol(org.jgroups.stack.Protocol p) { + this.prot=p; + } + + public String getPropertiesAsString() { + return prot.getProperties().toString(); + } + + public void setProperties(Properties p) { + prot.setProperties(p); + } + + + public boolean getStatsEnabled() { + return prot.statsEnabled(); + } + + public void setStatsEnabled(boolean flag) { + prot.enableStats(flag); + } + + public void resetStats() { + prot.resetStats(); + } + + public String printStats() { + return prot.printStats(); + } + + public Map dumpStats() { + return prot.dumpStats(); + } + + public void create() throws Exception { + prot.init(); + } + + public void start() throws Exception { + prot.start(); + } + + public void stop() { + prot.stop(); + } + + public void destroy() { + prot.destroy(); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/ProtocolMBean.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/ProtocolMBean.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/ProtocolMBean.java 17 Aug 2012 14:51:26 -0000 1.1 @@ -0,0 +1,24 @@ +package org.jgroups.jmx; + +import java.util.Map; +import java.util.Properties; + +/** + * @author Bela Ban + * @version $Id: ProtocolMBean.java,v 1.1 2012/08/17 14:51:26 marcin Exp $ + */ +public interface ProtocolMBean { + String getName(); + String getPropertiesAsString(); + void setProperties(Properties p); + boolean getStatsEnabled(); + void setStatsEnabled(boolean flag); + void resetStats(); + String printStats(); + Map dumpStats(); + void create() throws Exception; + void start() throws Exception; + void stop(); + void destroy(); + +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/package.html =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/package.html,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/package.html 17 Aug 2012 14:51:26 -0000 1.1 @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/BARRIER.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/BARRIER.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/BARRIER.java 17 Aug 2012 14:51:00 -0000 1.1 @@ -0,0 +1,41 @@ +package org.jgroups.jmx.protocols; + +import org.jgroups.jmx.Protocol; + +/** + * JMX wrapper for BARRIER protocol. + * + * @author rpike + */ +public class BARRIER extends Protocol implements BARRIERMBean { + private org.jgroups.protocols.BARRIER p; + + public BARRIER() { + } + + public BARRIER(org.jgroups.stack.Protocol p) { + super(p); + this.p=(org.jgroups.protocols.BARRIER)p; + } + + public void attachProtocol(org.jgroups.stack.Protocol p) { + super.attachProtocol(p); + this.p=(org.jgroups.protocols.BARRIER)p; + } + + public int getInFlightThreadsCount() { + return p.getNumberOfInFlightThreads(); + } + + public long getMaxCloseTime() { + return p.getMaxCloseTime(); + } + + public boolean isClosed() { + return p.isClosed(); + } + + public boolean isOpenerScheduled() { + return p.isOpenerScheduled(); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/BARRIERMBean.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/BARRIERMBean.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/BARRIERMBean.java 17 Aug 2012 14:51:00 -0000 1.1 @@ -0,0 +1,28 @@ +package org.jgroups.jmx.protocols; + +import org.jgroups.jmx.ProtocolMBean; + +/** + * JMX interface for BARRIER protocol. + * + * @author rpike + */ +public interface BARRIERMBean extends ProtocolMBean { + + /** Indicates if barrier is currently closed. */ + public boolean isClosed(); + + /** Gets configured max_close_time value (ms). */ + public long getMaxCloseTime(); + + /** Returns true if barrier_opener_future is non-null. */ + public boolean isOpenerScheduled(); + + /** + * Returns the current count of in-flight threads. + *

In-flight threads are those currently processing in higher-level protocols. + * + * @return in-flight threads count + */ + public int getInFlightThreadsCount(); +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/Discovery.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/Discovery.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/Discovery.java 17 Aug 2012 14:51:00 -0000 1.1 @@ -0,0 +1,62 @@ +package org.jgroups.jmx.protocols; + +import org.jgroups.jmx.Protocol; + +import java.util.Vector; + +/** + * @author Bela Ban + * @version $Id: Discovery.java,v 1.1 2012/08/17 14:51:00 marcin Exp $ + */ +public class Discovery extends Protocol implements DiscoveryMBean { + org.jgroups.protocols.Discovery p; + + public Discovery() { + } + + public Discovery(org.jgroups.stack.Protocol p) { + super(p); + this.p=(org.jgroups.protocols.Discovery)p; + } + + public void attachProtocol(org.jgroups.stack.Protocol p) { + super.attachProtocol(p); + this.p=(org.jgroups.protocols.Discovery)p; + } + + public long getTimeout() { + return p.getTimeout(); + } + + public void setTimeout(long timeout) { + p.setTimeout(timeout); + } + + public int getInitialMembers() { + return p.getNumInitialMembers(); + } + + public void setInitialMembers(int num_initial_members) { + p.setNumInitialMembers(num_initial_members); + } + + public int getPingRequests() { + return p.getNumPingRequests(); + } + + public void setPingRequests(int num_ping_requests) { + p.setNumPingRequests(num_ping_requests); + } + + public int getDiscoveryRequestsSent() { + return p.getNumberOfDiscoveryRequestsSent(); + } + + public Vector findInitialMembers() { + return new Vector(p.findInitialMembers(null)); + } + + public String findInitialMembersAsString() { + return p.findInitialMembersAsString(); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/DiscoveryMBean.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/DiscoveryMBean.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/DiscoveryMBean.java 17 Aug 2012 14:51:00 -0000 1.1 @@ -0,0 +1,21 @@ +package org.jgroups.jmx.protocols; + +import org.jgroups.jmx.ProtocolMBean; + +import java.util.Vector; + +/** + * @author Bela Ban + * @version $Id: DiscoveryMBean.java,v 1.1 2012/08/17 14:51:00 marcin Exp $ + */ +public interface DiscoveryMBean extends ProtocolMBean { + long getTimeout(); + void setTimeout(long timeout); + int getInitialMembers(); + void setInitialMembers(int num_initial_members); + int getPingRequests(); + void setPingRequests(int num_ping_requests); + int getDiscoveryRequestsSent(); + Vector findInitialMembers(); + String findInitialMembersAsString(); +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/FC.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/FC.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/FC.java 17 Aug 2012 14:51:00 -0000 1.1 @@ -0,0 +1,106 @@ +package org.jgroups.jmx.protocols; + +import org.jgroups.jmx.Protocol; + +/** + * @author Bela Ban + * @version $Id: FC.java,v 1.1 2012/08/17 14:51:00 marcin Exp $ + */ +public class FC extends Protocol implements FCMBean { + org.jgroups.protocols.FC p; + + public FC() { + } + + public FC(org.jgroups.stack.Protocol p) { + super(p); + this.p=(org.jgroups.protocols.FC)p; + } + + public void attachProtocol(org.jgroups.stack.Protocol p) { + super.attachProtocol(p); + this.p=(org.jgroups.protocols.FC)p; + } + + public long getMaxCredits() { + return p.getMaxCredits(); + } + + public void setMaxCredits(long max_credits) { + p.setMaxCredits(max_credits); + } + + public double getMinThreshold() { + return p.getMinThreshold(); + } + + public void setMinThreshold(double min_threshold) { + p.setMinThreshold(min_threshold); + } + + public long getMinCredits() { + return p.getMinCredits(); + } + + public void setMinCredits(long min_credits) { + p.setMinCredits(min_credits); + } + + + public int getBlockings() { + return p.getNumberOfBlockings(); + } + + public long getTotalTimeBlocked() { + return p.getTotalTimeBlocked(); + } + + public long getMaxBlockTime() { + return p.getMaxBlockTime(); + } + + public void setMaxBlockTime(long t) { + p.setMaxBlockTime(t); + } + + public double getAverageTimeBlocked() { + return p.getAverageTimeBlocked(); + } + + public int getCreditRequestsReceived() { + return p.getNumberOfCreditRequestsReceived(); + } + + public int getCreditRequestsSent() { + return p.getNumberOfCreditRequestsSent(); + } + + public int getCreditResponsesReceived() { + return p.getNumberOfCreditResponsesReceived(); + } + + public int getCreditResponsesSent() { + return p.getNumberOfCreditResponsesSent(); + } + + + public String printSenderCredits() { + return p.printSenderCredits(); + } + + public String printReceiverCredits() { + return p.printReceiverCredits(); + } + + public String printCredits() { + return p.printCredits(); + } + + public String showLastBlockingTimes() { + return p.showLastBlockingTimes(); + } + + public void unblock() { + p.unblock(); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/FCMBean.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/FCMBean.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/FCMBean.java 17 Aug 2012 14:50:59 -0000 1.1 @@ -0,0 +1,30 @@ +package org.jgroups.jmx.protocols; + +import org.jgroups.jmx.ProtocolMBean; + +/** + * @author Bela Ban + * @version $Id: FCMBean.java,v 1.1 2012/08/17 14:50:59 marcin Exp $ + */ +public interface FCMBean extends ProtocolMBean { + long getMaxCredits(); + void setMaxCredits(long max_credits); + double getMinThreshold(); + void setMinThreshold(double min_threshold); + long getMinCredits(); + void setMinCredits(long min_credits); + int getBlockings(); + long getTotalTimeBlocked(); + long getMaxBlockTime(); + void setMaxBlockTime(long t); + double getAverageTimeBlocked(); + int getCreditRequestsReceived(); + int getCreditRequestsSent(); + int getCreditResponsesReceived(); + int getCreditResponsesSent(); + String printSenderCredits(); + String printReceiverCredits(); + String printCredits(); + String showLastBlockingTimes(); + void unblock(); +} \ No newline at end of file Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/FD.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/FD.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/FD.java 17 Aug 2012 14:50:59 -0000 1.1 @@ -0,0 +1,80 @@ +package org.jgroups.jmx.protocols; + +import org.jgroups.jmx.Protocol; + +/** + * @author Bela Ban + * @version $Id: FD.java,v 1.1 2012/08/17 14:50:59 marcin Exp $ + */ +public class FD extends Protocol implements FDMBean { + org.jgroups.protocols.FD p; + + public FD() { + } + + public FD(org.jgroups.stack.Protocol p) { + super(p); + this.p=(org.jgroups.protocols.FD)p; + } + + public void attachProtocol(org.jgroups.stack.Protocol p) { + super.attachProtocol(p); + this.p=(org.jgroups.protocols.FD)p; + } + + public int getNumberOfHeartbeatsSent() { + return p.getNumberOfHeartbeatsSent(); + } + + public int getNumSuspectEventsGenerated() { + return p.getNumSuspectEventsGenerated(); + } + + public long getTimeout() { + return p.getTimeout(); + } + + public void setTimeout(long timeout) { + p.setTimeout(timeout); + } + + public int getMaxTries() { + return p.getMaxTries(); + } + + public void setMaxTries(int max_tries) { + p.setMaxTries(max_tries); + } + + public int getCurrentNumTries() { + return p.getCurrentNumTries(); + } + + public boolean isShun() { + return p.isShun(); + } + + public void setShun(boolean flag) { + p.setShun(flag); + } + + public String getLocalAddress() { + return p.getLocalAddress(); + } + + public String getMembers() { + return p.getMembers(); + } + + public String getPingableMembers() { + return p.getPingableMembers(); + } + + public String getPingDest() { + return p.getPingDest(); + } + + public String printSuspectHistory() { + return p.printSuspectHistory(); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/FDMBean.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/FDMBean.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/FDMBean.java 17 Aug 2012 14:50:59 -0000 1.1 @@ -0,0 +1,24 @@ +package org.jgroups.jmx.protocols; + +import org.jgroups.jmx.ProtocolMBean; + +/** + * @author Bela Ban + * @version $Id: FDMBean.java,v 1.1 2012/08/17 14:50:59 marcin Exp $ + */ +public interface FDMBean extends ProtocolMBean { + int getNumberOfHeartbeatsSent(); + int getNumSuspectEventsGenerated(); + String getLocalAddress(); + String getMembers(); + String getPingableMembers(); + String getPingDest(); + long getTimeout(); + void setTimeout(long timeout); + int getMaxTries(); + void setMaxTries(int max_tries); + int getCurrentNumTries(); + boolean isShun(); + void setShun(boolean flag); + String printSuspectHistory(); +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/FD_ALL.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/FD_ALL.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/FD_ALL.java 17 Aug 2012 14:50:59 -0000 1.1 @@ -0,0 +1,80 @@ +package org.jgroups.jmx.protocols; + +import org.jgroups.jmx.Protocol; + +/** + * @author Bela Ban + * @version $Id: FD_ALL.java,v 1.1 2012/08/17 14:50:59 marcin Exp $ + */ +public class FD_ALL extends Protocol implements FD_ALLMBean { + org.jgroups.protocols.FD_ALL p; + + public FD_ALL() { + } + + public FD_ALL(org.jgroups.stack.Protocol p) { + super(p); + this.p=(org.jgroups.protocols.FD_ALL)p; + } + + public void attachProtocol(org.jgroups.stack.Protocol p) { + super.attachProtocol(p); + this.p=(org.jgroups.protocols.FD_ALL)p; + } + + public int getHeartbeatsSent() { + return p.getHeartbeatsSent(); + } + + public int getHeartbeatsReceived() { + return p.getHeartbeatsReceived(); + } + + public int getSuspectEventsSent() { + return p.getSuspectEventsSent(); + } + + public long getTimeout() { + return p.getTimeout(); + } + + public void setTimeout(long timeout) { + p.setTimeout(timeout); + } + + public long getInterval() { + return p.getInterval(); + } + + public void setInterval(long interval) { + p.setInterval(interval); + } + + public boolean isShun() { + return p.isShun(); + } + + public void setShun(boolean flag) { + p.setShun(flag); + } + + public boolean isRunning() { + return p.isRunning(); + } + + public String getLocalAddress() { + return p.getLocalAddress(); + } + + public String getMembers() { + return p.getMembers(); + } + + public String printSuspectHistory() { + return p.printSuspectHistory(); + } + + public String printTimestamps() { + return p.printTimestamps(); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/FD_ALLMBean.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/FD_ALLMBean.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/FD_ALLMBean.java 17 Aug 2012 14:51:00 -0000 1.1 @@ -0,0 +1,24 @@ +package org.jgroups.jmx.protocols; + +import org.jgroups.jmx.ProtocolMBean; + +/** + * @author Bela Ban + * @version $Id: FD_ALLMBean.java,v 1.1 2012/08/17 14:51:00 marcin Exp $ + */ +public interface FD_ALLMBean extends ProtocolMBean { + int getHeartbeatsSent(); + int getHeartbeatsReceived(); + int getSuspectEventsSent(); + String getLocalAddress(); + String getMembers(); + long getTimeout(); + void setTimeout(long timeout); + long getInterval(); + void setInterval(long interval); + boolean isShun(); + void setShun(boolean flag); + boolean isRunning(); + String printSuspectHistory(); + String printTimestamps(); +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/FD_SOCK.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/FD_SOCK.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/FD_SOCK.java 17 Aug 2012 14:50:58 -0000 1.1 @@ -0,0 +1,53 @@ +package org.jgroups.jmx.protocols; + +import org.jgroups.jmx.Protocol; + +/** + * @author Bela Ban + * @version $Id: FD_SOCK.java,v 1.1 2012/08/17 14:50:58 marcin Exp $ + */ +public class FD_SOCK extends Protocol implements FD_SOCKMBean { + org.jgroups.protocols.FD_SOCK p; + + public FD_SOCK() { + } + + public FD_SOCK(org.jgroups.stack.Protocol p) { + super(p); + this.p=(org.jgroups.protocols.FD_SOCK)p; + } + + public void attachProtocol(org.jgroups.stack.Protocol p) { + super.attachProtocol(p); + this.p=(org.jgroups.protocols.FD_SOCK)p; + } + + + public String getLocalAddress() { + return p.getLocalAddress(); + } + + public String getMembers() { + return p.getMembers(); + } + + public String getPingableMembers() { + return p.getPingableMembers(); + } + + public String getPingDest() { + return p.getPingDest(); + } + + public int getNumSuspectEventsGenerated() { + return p.getNumSuspectEventsGenerated(); + } + + public String printSuspectHistory() { + return p.printSuspectHistory(); + } + + public String printCache() { + return p.printCache(); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/FD_SOCKMBean.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/FD_SOCKMBean.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/FD_SOCKMBean.java 17 Aug 2012 14:50:59 -0000 1.1 @@ -0,0 +1,20 @@ +package org.jgroups.jmx.protocols; + +import org.jgroups.jmx.ProtocolMBean; + +import java.util.Enumeration; +import java.util.Date; + +/** + * @author Bela Ban + * @version $Id: FD_SOCKMBean.java,v 1.1 2012/08/17 14:50:59 marcin Exp $ + */ +public interface FD_SOCKMBean extends ProtocolMBean { + String getLocalAddress(); + String getMembers(); + String getPingableMembers(); + String getPingDest(); + int getNumSuspectEventsGenerated(); + String printSuspectHistory(); + String printCache(); +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/FRAG.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/FRAG.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/FRAG.java 17 Aug 2012 14:50:59 -0000 1.1 @@ -0,0 +1,48 @@ +package org.jgroups.jmx.protocols; + +import org.jgroups.jmx.Protocol; + +/** + * @author Bela Ban + * @version $Id: FRAG.java,v 1.1 2012/08/17 14:50:59 marcin Exp $ + */ +public class FRAG extends Protocol implements FRAGMBean { + org.jgroups.protocols.FRAG p; + + public FRAG() { + } + + public FRAG(org.jgroups.stack.Protocol p) { + super(p); + this.p=(org.jgroups.protocols.FRAG)p; + } + + public void attachProtocol(org.jgroups.stack.Protocol p) { + super.attachProtocol(p); + this.p=(org.jgroups.protocols.FRAG)p; + } + + public int getFragSize() { + return p.getFragSize(); + } + + public void setFragSize(int s) { + p.setFragSize(s); + } + + public long getNumberOfSentMessages() { + return p.getNumberOfSentMessages(); + } + + public long getNumberOfSentFragments() { + return p.getNumberOfSentFragments(); + } + + public long getNumberOfReceivedMessages() { + return p.getNumberOfReceivedMessages(); + } + + public long getNumberOfReceivedFragments() { + return p.getNumberOfReceivedFragments(); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/FRAG2.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/FRAG2.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/FRAG2.java 17 Aug 2012 14:50:59 -0000 1.1 @@ -0,0 +1,56 @@ +package org.jgroups.jmx.protocols; + +import org.jgroups.jmx.Protocol; + +/** + * @author Bela Ban + * @version $Id: FRAG2.java,v 1.1 2012/08/17 14:50:59 marcin Exp $ + */ +public class FRAG2 extends Protocol implements FRAG2MBean { + org.jgroups.protocols.FRAG2 p; + + public FRAG2() { + } + + public FRAG2(org.jgroups.stack.Protocol p) { + super(p); + this.p=(org.jgroups.protocols.FRAG2)p; + } + + public void attachProtocol(org.jgroups.stack.Protocol p) { + super.attachProtocol(p); + this.p=(org.jgroups.protocols.FRAG2)p; + } + + public int getFragSize() { + return p.getFragSize(); + } + + public void setFragSize(int s) { + p.setFragSize(s); + } + + public int getOverhead() { + return p.getOverhead(); + } + + public void setOverhead(int o) { + p.setOverhead(o); + } + + public long getNumberOfSentMessages() { + return p.getNumberOfSentMessages(); + } + + public long getNumberOfSentFragments() { + return p.getNumberOfSentFragments(); + } + + public long getNumberOfReceivedMessages() { + return p.getNumberOfReceivedMessages(); + } + + public long getNumberOfReceivedFragments() { + return p.getNumberOfReceivedFragments(); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/FRAG2MBean.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/FRAG2MBean.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/FRAG2MBean.java 17 Aug 2012 14:50:59 -0000 1.1 @@ -0,0 +1,18 @@ +package org.jgroups.jmx.protocols; + +import org.jgroups.jmx.ProtocolMBean; + +/** + * @author Bela Ban + * @version $Id: FRAG2MBean.java,v 1.1 2012/08/17 14:50:59 marcin Exp $ + */ +public interface FRAG2MBean extends ProtocolMBean { + int getFragSize(); + void setFragSize(int s); + int getOverhead(); + void setOverhead(int o); + long getNumberOfSentMessages(); + long getNumberOfSentFragments(); + long getNumberOfReceivedMessages(); + long getNumberOfReceivedFragments(); +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/FRAGMBean.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/FRAGMBean.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/FRAGMBean.java 17 Aug 2012 14:51:00 -0000 1.1 @@ -0,0 +1,16 @@ +package org.jgroups.jmx.protocols; + +import org.jgroups.jmx.ProtocolMBean; + +/** + * @author Bela Ban + * @version $Id: FRAGMBean.java,v 1.1 2012/08/17 14:51:00 marcin Exp $ + */ +public interface FRAGMBean extends ProtocolMBean { + int getFragSize(); + void setFragSize(int s); + long getNumberOfSentMessages(); + long getNumberOfSentFragments(); + long getNumberOfReceivedMessages(); + long getNumberOfReceivedFragments(); +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/MERGE2.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/MERGE2.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/MERGE2.java 17 Aug 2012 14:50:59 -0000 1.1 @@ -0,0 +1,40 @@ +package org.jgroups.jmx.protocols; + +import org.jgroups.jmx.Protocol; + +/** + * @author Bela Ban + * @version $Id: MERGE2.java,v 1.1 2012/08/17 14:50:59 marcin Exp $ + */ +public class MERGE2 extends Protocol implements MERGE2MBean { + org.jgroups.protocols.MERGE2 p; + + public MERGE2() { + } + + public MERGE2(org.jgroups.stack.Protocol p) { + super(p); + this.p=(org.jgroups.protocols.MERGE2)p; + } + + public void attachProtocol(org.jgroups.stack.Protocol p) { + super.attachProtocol(p); + this.p=(org.jgroups.protocols.MERGE2)p; + } + + public long getMinInterval() { + return p.getMinInterval(); + } + + public void setMinInterval(long i) { + p.setMinInterval(i); + } + + public long getMaxInterval() { + return p.getMaxInterval(); + } + + public void setMaxInterval(long l) { + p.setMaxInterval(l); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/MERGE2MBean.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/MERGE2MBean.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/MERGE2MBean.java 17 Aug 2012 14:51:00 -0000 1.1 @@ -0,0 +1,14 @@ +package org.jgroups.jmx.protocols; + +import org.jgroups.jmx.ProtocolMBean; + +/** + * @author Bela Ban + * @version $Id: MERGE2MBean.java,v 1.1 2012/08/17 14:51:00 marcin Exp $ + */ +public interface MERGE2MBean extends ProtocolMBean { + long getMinInterval(); + void setMinInterval(long i); + long getMaxInterval(); + void setMaxInterval(long l); +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/MPING.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/MPING.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/MPING.java 17 Aug 2012 14:51:00 -0000 1.1 @@ -0,0 +1,76 @@ +package org.jgroups.jmx.protocols; + +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.util.List; + +/** + * @author Bela Ban + * @version $Id: MPING.java,v 1.1 2012/08/17 14:51:00 marcin Exp $ + */ +public class MPING extends PING implements MPINGMBean { + org.jgroups.protocols.MPING mping; + + public MPING() { + } + + public MPING(org.jgroups.stack.Protocol p) { + super(p); + this.mping=(org.jgroups.protocols.MPING)p; + } + + public void attachProtocol(org.jgroups.stack.Protocol p) { + super.attachProtocol(p); + this.mping=(org.jgroups.protocols.MPING)p; + } + + public InetAddress getBindAddr() { + return mping.getBindAddr(); + } + + public void setBindAddr(InetAddress bind_addr) { + mping.setBindAddr(bind_addr); + } + + + public List getReceiveInterfaces() { + return mping.getReceiveInterfaces(); + } + + public List getSendInterfaces() { + return mping.getSendInterfaces(); + } + + public boolean isReceiveOnAllInterfaces() { + return mping.isReceiveOnAllInterfaces(); + } + + public boolean isSendOnAllInterfaces() { + return mping.isSendOnAllInterfaces(); + } + + public int getTTL() { + return mping.getTTL(); + } + + public void setTTL(int ip_ttl) { + mping.setTTL(ip_ttl); + } + + public InetAddress getMcastAddr() { + return mping.getMcastAddr(); + } + + public void setMcastAddr(InetAddress mcast_addr) { + mping.setMcastAddr(mcast_addr); + } + + public int getMcastPort() { + return mping.getMcastPort(); + } + + public void setMcastPort(int mcast_port) { + mping.setMcastPort(mcast_port); + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/MPINGMBean.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/MPINGMBean.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/MPINGMBean.java 17 Aug 2012 14:50:58 -0000 1.1 @@ -0,0 +1,25 @@ +package org.jgroups.jmx.protocols; + +import org.jgroups.jmx.ProtocolMBean; + +import java.net.InetAddress; +import java.util.List; + +/** + * @author Bela Ban + * @version $Id: MPINGMBean.java,v 1.1 2012/08/17 14:50:58 marcin Exp $ + */ +public interface MPINGMBean extends PINGMBean { + InetAddress getBindAddr(); + void setBindAddr(InetAddress bind_addr); + boolean isReceiveOnAllInterfaces(); + List getReceiveInterfaces(); + boolean isSendOnAllInterfaces(); + List getSendInterfaces(); + int getTTL(); + void setTTL(int ip_ttl); + InetAddress getMcastAddr(); + void setMcastAddr(InetAddress mcast_addr); + int getMcastPort(); + void setMcastPort(int mcast_port); +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/PARTITION.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/PARTITION.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/PARTITION.java 17 Aug 2012 14:50:59 -0000 1.1 @@ -0,0 +1,37 @@ +package org.jgroups.jmx.protocols; + +import org.jgroups.jmx.Protocol; + +/** + * @author Bela Ban + * @version $Id: PARTITION.java,v 1.1 2012/08/17 14:50:59 marcin Exp $ + */ +public class PARTITION extends Protocol implements PARTITIONMBean { + org.jgroups.protocols.PARTITION partiton; + + public PARTITION() { + } + + public PARTITION(org.jgroups.stack.Protocol p) { + super(p); + this.partiton=(org.jgroups.protocols.PARTITION)p; + } + + public void attachProtocol(org.jgroups.stack.Protocol p) { + super.attachProtocol(p); + this.partiton=(org.jgroups.protocols.PARTITION)p; + } + + + public boolean isPartitionOn() { + return partiton.isPartitionOn(); + } + + public void startPartition() { + partiton.startPartition(); + } + + public void stopPartition() { + partiton.stopPartition(); + } +} \ No newline at end of file Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/PARTITIONMBean.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/PARTITIONMBean.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/PARTITIONMBean.java 17 Aug 2012 14:51:00 -0000 1.1 @@ -0,0 +1,16 @@ +package org.jgroups.jmx.protocols; + +import org.jgroups.jmx.ProtocolMBean; + +import java.net.InetAddress; +import java.util.List; + +/** + * @author Bela Ban + * @version $Id: PARTITIONMBean.java,v 1.1 2012/08/17 14:51:00 marcin Exp $ + */ +public interface PARTITIONMBean extends ProtocolMBean { + boolean isPartitionOn(); + void startPartition(); + void stopPartition(); +} \ No newline at end of file Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/PING.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/PING.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/PING.java 17 Aug 2012 14:51:00 -0000 1.1 @@ -0,0 +1,23 @@ +package org.jgroups.jmx.protocols; + +import org.jgroups.jmx.Protocol; + +/** + * @author Bela Ban + * @version $Id: PING.java,v 1.1 2012/08/17 14:51:00 marcin Exp $ + */ +public class PING extends Discovery implements PINGMBean { + + public PING() { + } + + public PING(org.jgroups.stack.Protocol p) { + super(p); + this.p=(org.jgroups.protocols.PING)p; + } + + public void attachProtocol(org.jgroups.stack.Protocol p) { + super.attachProtocol(p); + this.p=(org.jgroups.protocols.PING)p; + } +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/PINGMBean.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/PINGMBean.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/PINGMBean.java 17 Aug 2012 14:51:00 -0000 1.1 @@ -0,0 +1,10 @@ +package org.jgroups.jmx.protocols; + +import org.jgroups.jmx.ProtocolMBean; + +/** + * @author Bela Ban + * @version $Id: PINGMBean.java,v 1.1 2012/08/17 14:51:00 marcin Exp $ + */ +public interface PINGMBean extends DiscoveryMBean { +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/SEQUENCER.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/SEQUENCER.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/SEQUENCER.java 17 Aug 2012 14:50:59 -0000 1.1 @@ -0,0 +1,66 @@ +package org.jgroups.jmx.protocols; + +import org.jgroups.jmx.Protocol; + +import java.util.Map; + +/** + * @author Bela Ban + * @version $Id: SEQUENCER.java,v 1.1 2012/08/17 14:50:59 marcin Exp $ + */ +public class SEQUENCER extends Protocol implements SEQUENCERMBean { + org.jgroups.protocols.SEQUENCER p; + + public SEQUENCER() { + } + + public SEQUENCER(org.jgroups.stack.Protocol p) { + super(p); + this.p=(org.jgroups.protocols.SEQUENCER)p; + } + + public void attachProtocol(org.jgroups.stack.Protocol p) { + super.attachProtocol(p); + this.p=(org.jgroups.protocols.SEQUENCER)p; + } + + public boolean isCoord() { + return p.isCoordinator(); + } + + public String getCoordinator() { + return p.getCoordinator().toString(); + } + + public String getLocalAddress() { + return p.getLocalAddress().toString(); + } + + public long getForwarded() { + return p.getForwarded(); + } + + public long getBroadcast() { + return p.getBroadcast(); + } + + public long getReceivedForwards() { + return p.getReceivedForwards(); + } + + public long getReceivedBroadcasts() { + return p.getReceivedBroadcasts(); + } + + public void resetStats() { + super.resetStats(); + } + + public String printStats() { + return super.printStats(); + } + + public Map dumpStats() { + return super.dumpStats(); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/SEQUENCERMBean.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/SEQUENCERMBean.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/SEQUENCERMBean.java 17 Aug 2012 14:50:59 -0000 1.1 @@ -0,0 +1,18 @@ +package org.jgroups.jmx.protocols; + +import org.jgroups.jmx.ProtocolMBean; + +/** + * @author Bela Ban + * @version $Id: SEQUENCERMBean.java,v 1.1 2012/08/17 14:50:59 marcin Exp $ + */ +public interface SEQUENCERMBean extends ProtocolMBean { + boolean isCoord(); + String getCoordinator(); + String getLocalAddress(); + long getForwarded(); + long getBroadcast(); + long getReceivedForwards(); + long getReceivedBroadcasts(); + +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/SFC.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/SFC.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/SFC.java 17 Aug 2012 14:51:00 -0000 1.1 @@ -0,0 +1,95 @@ +package org.jgroups.jmx.protocols; + +import org.jgroups.jmx.Protocol; + +import java.util.Map; + +/** + * @author Bela Ban + * @version $Id: SFC.java,v 1.1 2012/08/17 14:51:00 marcin Exp $ + */ +public class SFC extends Protocol implements SFCMBean { + org.jgroups.protocols.SFC p; + + public SFC() { + } + + public SFC(org.jgroups.stack.Protocol p) { + super(p); + this.p=(org.jgroups.protocols.SFC)p; + } + + public void attachProtocol(org.jgroups.stack.Protocol p) { + super.attachProtocol(p); + this.p=(org.jgroups.protocols.SFC)p; + } + + public void resetStats() { + super.resetStats(); + p.resetStats(); + } + + public long getMaxCredits() { + return p.getMaxCredits(); + } + + public long getBytesSent() { + return p.getBytesSent(); + } + + public long getCredits() { + return p.getCredits(); + } + + public long getBlockings() { + return p.getBlockings(); + } + + public long getCreditRequestsReceived() { + return p.getCreditRequestsReceived(); + } + + public long getCreditRequestsSent() { + return p.getCreditRequestsSent(); + } + + public long getReplenishmentsReceived() { + return p.getReplenishmentsReceived(); + } + + public long getReplenishmentsSent() { + return p.getReplenishmentsSent(); + } + + public long getTotalBlockingTime() { + return p.getTotalBlockingTime(); + } + + public double getAverageBlockingTime() { + return p.getAverageBlockingTime(); + } + + public Map dumpStats() { + return p.dumpStats(); + } + + public String printBlockingTimes() { + return p.printBlockingTimes(); + } + + public String printReceived() { + return p.printReceived(); + } + + public String printPendingCreditors() { + return p.printPendingCreditors(); + } + + public String printPendingRequesters() { + return p.printPendingRequesters(); + } + + public void unblock() { + p.unblock(); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/SFCMBean.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/SFCMBean.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/SFCMBean.java 17 Aug 2012 14:50:59 -0000 1.1 @@ -0,0 +1,25 @@ +package org.jgroups.jmx.protocols; + +import org.jgroups.jmx.ProtocolMBean; + +/** + * @author Bela Ban + * @version $Id: SFCMBean.java,v 1.1 2012/08/17 14:50:59 marcin Exp $ + */ +public interface SFCMBean extends ProtocolMBean { + long getMaxCredits(); + long getCredits(); + long getBytesSent(); + long getBlockings(); + long getCreditRequestsReceived(); + long getCreditRequestsSent(); + long getReplenishmentsReceived(); + long getReplenishmentsSent(); + long getTotalBlockingTime(); + double getAverageBlockingTime(); + String printBlockingTimes(); + String printReceived(); + String printPendingCreditors(); + String printPendingRequesters(); + void unblock(); +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/STATS.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/STATS.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/STATS.java 17 Aug 2012 14:51:00 -0000 1.1 @@ -0,0 +1,77 @@ +package org.jgroups.jmx.protocols; + +import org.jgroups.stack.Protocol; +import org.jgroups.protocols.*; + +/** + * @author Bela Ban + * @version $Id: STATS.java,v 1.1 2012/08/17 14:51:00 marcin Exp $ + */ +public class STATS extends org.jgroups.jmx.Protocol implements STATSMBean { + org.jgroups.protocols.STATS p; + + public STATS() { + } + + public STATS(Protocol p) { + super(p); + this.p=(org.jgroups.protocols.STATS)p; + } + + public void attachProtocol(Protocol p) { + super.attachProtocol(p); + this.p=(org.jgroups.protocols.STATS)p; + } + + public long getSentMessages() { + return p.getSentMessages(); + } + + public long getSentBytes() { + return p.getSentBytes(); + } + + public long getSentUnicastMessages() { + return p.getSentUnicastMessages(); + } + + public long getSentUnicastBytes() { + return p.getSentUnicastBytes(); + } + + public long getSentMcastMessages() { + return p.getSentMcastMessages(); + } + + public long getSentMcastBytes() { + return p.getSentMcastBytes(); + } + + public long getReceivedMessages() { + return p.getReceivedMessages(); + } + + public long getReceivedBytes() { + return p.getReceivedBytes(); + } + + public long getReceivedUnicastMessages() { + return p.getReceivedUnicastMessages(); + } + + public long getReceivedUnicastBytes() { + return p.getReceivedUnicastBytes(); + } + + public long getReceivedMcastMessages() { + return p.getReceivedMcastMessages(); + } + + public long getReceivedMcastBytes() { + return p.getReceivedMcastBytes(); + } + + public String printStats() { + return p.printStats(); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/STATSMBean.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/STATSMBean.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/STATSMBean.java 17 Aug 2012 14:51:00 -0000 1.1 @@ -0,0 +1,23 @@ +package org.jgroups.jmx.protocols; + +import org.jgroups.jmx.ProtocolMBean; + +/** + * @author Bela Ban + * @version $Id: STATSMBean.java,v 1.1 2012/08/17 14:51:00 marcin Exp $ + */ +public interface STATSMBean extends ProtocolMBean { + long getSentMessages(); + long getSentBytes(); + long getSentUnicastMessages(); + long getSentUnicastBytes(); + long getSentMcastMessages(); + long getSentMcastBytes(); + long getReceivedMessages(); + long getReceivedBytes(); + long getReceivedUnicastMessages(); + long getReceivedUnicastBytes(); + long getReceivedMcastMessages(); + long getReceivedMcastBytes(); + String printStats(); +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/TCP.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/TCP.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/TCP.java 17 Aug 2012 14:50:58 -0000 1.1 @@ -0,0 +1,80 @@ +package org.jgroups.jmx.protocols; + +import java.net.UnknownHostException; + +/** + * @author Bela Ban + * @version $Id: TCP.java,v 1.1 2012/08/17 14:50:58 marcin Exp $ + */ +public class TCP extends TP implements TCPMBean { + org.jgroups.protocols.TCP p; + + public TCP() { + } + + public TCP(org.jgroups.stack.Protocol p) { + super(p); + this.p=(org.jgroups.protocols.TCP)p; + } + + public void attachProtocol(org.jgroups.stack.Protocol p) { + super.attachProtocol(p); + this.p=(org.jgroups.protocols.TCP)p; + } + + + public int getOpenConnections() { + return p.getOpenConnections(); + } + + public String getBindAddr() { + return p.getBindAddress(); + } + + public void setBindAddr(String bind_addr) { + try { + p.setBindAddress(bind_addr); + } + catch(UnknownHostException e) { + IllegalArgumentException iae = new IllegalArgumentException("Unknown host " + bind_addr); + iae.initCause(e); + throw iae; + } + } + + public int getStartPort() { + return p.getStartPort(); + } + + public void setStartPort(int start_port) { + p.setStartPort(start_port); + } + + public int getEndPort() { + return p.getEndPort(); + } + + public void setEndPort(int end_port) { + p.setEndPort(end_port); + } + + public long getReaperInterval() { + return p.getReaperInterval(); + } + + public void setReaperInterval(long reaper_interval) { + p.setReaperInterval(reaper_interval); + } + + public long getConnExpireTime() { + return p.getConnExpireTime(); + } + + public void setConnExpireTime(long conn_expire_time) { + p.setConnExpireTime(conn_expire_time); + } + + public String printConnections() { + return p.printConnections(); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/TCPGOSSIP.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/TCPGOSSIP.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/TCPGOSSIP.java 17 Aug 2012 14:50:59 -0000 1.1 @@ -0,0 +1,21 @@ +package org.jgroups.jmx.protocols; + +import org.jgroups.jmx.Protocol; + +/** + * @author Bela Ban + * @version $Id: TCPGOSSIP.java,v 1.1 2012/08/17 14:50:59 marcin Exp $ + */ +public class TCPGOSSIP extends Discovery implements TCPGOSSIPMBean { + + public TCPGOSSIP() { + } + + public TCPGOSSIP(org.jgroups.stack.Protocol p) { + super(p); + } + + public void attachProtocol(org.jgroups.stack.Protocol p) { + super.attachProtocol(p); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/TCPGOSSIPMBean.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/TCPGOSSIPMBean.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/TCPGOSSIPMBean.java 17 Aug 2012 14:51:00 -0000 1.1 @@ -0,0 +1,10 @@ +package org.jgroups.jmx.protocols; + +import org.jgroups.jmx.ProtocolMBean; + +/** + * @author Bela Ban + * @version $Id: TCPGOSSIPMBean.java,v 1.1 2012/08/17 14:51:00 marcin Exp $ + */ +public interface TCPGOSSIPMBean extends DiscoveryMBean { +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/TCPMBean.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/TCPMBean.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/TCPMBean.java 17 Aug 2012 14:50:59 -0000 1.1 @@ -0,0 +1,20 @@ +package org.jgroups.jmx.protocols; + +/** + * @author Bela Ban + * @version $Id: TCPMBean.java,v 1.1 2012/08/17 14:50:59 marcin Exp $ + */ +public interface TCPMBean extends TPMBean { + int getOpenConnections(); + String getBindAddr(); + void setBindAddr(String bind_addr); + int getStartPort(); + void setStartPort(int start_port); + int getEndPort(); + void setEndPort(int end_port); + long getReaperInterval(); + void setReaperInterval(long reaper_interval); + long getConnExpireTime(); + void setConnExpireTime(long conn_expire_time); + String printConnections(); +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/TCPPING.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/TCPPING.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/TCPPING.java 17 Aug 2012 14:50:59 -0000 1.1 @@ -0,0 +1,19 @@ +package org.jgroups.jmx.protocols; + +/** + * @author Bela Ban + * @version $Id: TCPPING.java,v 1.1 2012/08/17 14:50:59 marcin Exp $ + */ +public class TCPPING extends Discovery implements TCPPINGMBean { + + public TCPPING() { + } + + public TCPPING(org.jgroups.stack.Protocol p) { + super(p); + } + + public void attachProtocol(org.jgroups.stack.Protocol p) { + super.attachProtocol(p); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/TCPPINGMBean.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/TCPPINGMBean.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/TCPPINGMBean.java 17 Aug 2012 14:50:59 -0000 1.1 @@ -0,0 +1,10 @@ +package org.jgroups.jmx.protocols; + +import org.jgroups.jmx.ProtocolMBean; + +/** + * @author Bela Ban + * @version $Id: TCPPINGMBean.java,v 1.1 2012/08/17 14:50:59 marcin Exp $ + */ +public interface TCPPINGMBean extends DiscoveryMBean { +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/TCP_NIO.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/TCP_NIO.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/TCP_NIO.java 17 Aug 2012 14:50:59 -0000 1.1 @@ -0,0 +1,50 @@ +package org.jgroups.jmx.protocols; + +/** + * @author Scott Marlow + * @version $Id: TCP_NIO.java,v 1.1 2012/08/17 14:50:59 marcin Exp $ + */ +public class TCP_NIO extends TCP implements TCP_NIOMBean { + + org.jgroups.protocols.TCP_NIO my_p; + public TCP_NIO() { + } + + public TCP_NIO(org.jgroups.stack.Protocol p) { + super(p); + this.my_p=(org.jgroups.protocols.TCP_NIO)p; + } + + public void attachProtocol(org.jgroups.stack.Protocol p) { + this.my_p=(org.jgroups.protocols.TCP_NIO)p; + } + + public int getReaderThreads() { + return my_p.getReaderThreads(); + } + + public int getWriterThreads() { + return my_p.getWriterThreads(); + } + + public int getProcessorThreads() { + return my_p.getProcessorThreads(); + } + + public int getProcessorMinThreads() { + return my_p.getProcessorMinThreads(); + } + + public int getProcessorMaxThreads() { + return my_p.getProcessorMaxThreads(); + } + + public int getProcessorQueueSize() { + return my_p.getProcessorQueueSize(); + } + + public long getProcessorKeepAliveTime() { + return my_p.getProcessorKeepAliveTime(); + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/TCP_NIOMBean.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/TCP_NIOMBean.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/TCP_NIOMBean.java 17 Aug 2012 14:50:59 -0000 1.1 @@ -0,0 +1,16 @@ +package org.jgroups.jmx.protocols; + +/** + * @author Scott Marlow + * @version $Id: TCP_NIOMBean.java,v 1.1 2012/08/17 14:50:59 marcin Exp $ + */ +public interface TCP_NIOMBean extends TCPMBean { + + int getReaderThreads(); + int getWriterThreads(); + int getProcessorThreads(); + int getProcessorMinThreads(); + int getProcessorMaxThreads(); + int getProcessorQueueSize(); + long getProcessorKeepAliveTime(); +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/TP.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/TP.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/TP.java 17 Aug 2012 14:51:00 -0000 1.1 @@ -0,0 +1,207 @@ +package org.jgroups.jmx.protocols; + +import org.jgroups.Address; +import org.jgroups.stack.Protocol; + +import java.net.UnknownHostException; +import java.util.List; + +/** + * @author Bela Ban + * @version $Id: TP.java,v 1.1 2012/08/17 14:51:00 marcin Exp $ + */ +public class TP extends org.jgroups.jmx.Protocol implements TPMBean { + org.jgroups.protocols.TP tp; + + public TP() { + } + + public TP(Protocol p) { + super(p); + tp=(org.jgroups.protocols.TP)p; + } + + public void attachProtocol(Protocol p) { + super.attachProtocol(p); + tp=(org.jgroups.protocols.TP)p; + } + + public long getMessagesSent() { + return tp.getNumMessagesSent(); + } + + public long getMessagesReceived() { + return tp.getNumMessagesReceived(); + } + + public long getBytesSent() { + return tp.getNumBytesSent(); + } + + public long getBytesReceived() { + return tp.getNumBytesReceived(); + } + + public Address getLocalAddress() { + return tp.getLocalAddress(); + } + + public String getBindAddress() { + return tp.getBindAddress(); + } + + public String getChannelName() { + return tp.getChannelName(); + } + + public void setBindAddress(String bind_address) throws UnknownHostException { + tp.setBindAddress(bind_address); + } + + public boolean isReceiveOnAllInterfaces() { + return tp.isReceiveOnAllInterfaces(); + } + + public List getReceiveInterfaces() { + return tp.getReceiveInterfaces(); + } + + public boolean isSendOnAllInterfaces() { + return tp.isSendOnAllInterfaces(); + } + + public List getSendInterfaces() { + return tp.getSendInterfaces(); + } + + public boolean isDiscardIncompatiblePackets() { + return tp.isDiscardIncompatiblePackets(); + } + + public void setDiscardIncompatiblePackets(boolean flag) { + tp.setDiscardIncompatiblePackets(flag); + } + + public boolean isEnableBundling() { + return tp.isEnableBundling(); + } + + public void setEnableBundling(boolean flag) { + tp.setEnableBundling(flag); + } + + public int getMaxBundleSize() { + return tp.getMaxBundleSize(); + } + + public void setMaxBundleSize(int size) { + tp.setMaxBundleSize(size); + } + + public long getMaxBundleTimeout() { + return tp.getMaxBundleTimeout(); + } + + public void setMaxBundleTimeout(long timeout) { + tp.setMaxBundleTimeout(timeout); + } + + public boolean isLoopback() { + return tp.isLoopback(); + } + + public void setLoopback(boolean b) { + tp.setLoopback(b); + } + + public boolean isUseIncomingPacketHandler() { + return tp.isUseIncomingPacketHandler(); + } + + + + + public int getOOBMinPoolSize() { + return tp.getOOBMinPoolSize(); + } + + public void setOOBMinPoolSize(int size) { + tp.setOOBMinPoolSize(size); + } + + public int getOOBMaxPoolSize() { + return tp.getOOBMaxPoolSize(); + } + + public void setOOBMaxPoolSize(int size) { + tp.setOOBMaxPoolSize(size); + } + + public int getOOBPoolSize() { + return tp.getOOBPoolSize(); + } + + public long getOOBKeepAliveTime() { + return tp.getOOBKeepAliveTime(); + } + + public void setOOBKeepAliveTime(long time) { + tp.setOOBKeepAliveTime(time); + } + + public long getOOBMessages() { + return tp.getOOBMessages(); + } + + public int getOOBQueueSize() { + return tp.getOOBQueueSize(); + } + + public int getOOBMaxQueueSize() { + return tp.getOOBMaxQueueSize(); + } + + + + public int getIncomingMinPoolSize() { + return tp.getIncomingMinPoolSize(); + } + + public void setIncomingMinPoolSize(int size) { + tp.setIncomingMinPoolSize(size); + } + + public int getIncomingMaxPoolSize() { + return tp.getIncomingMaxPoolSize(); + } + + public void setIncomingMaxPoolSize(int size) { + tp.setIncomingMaxPoolSize(size); + } + + public int getIncomingPoolSize() { + return tp.getIncomingPoolSize(); + } + + public long getIncomingKeepAliveTime() { + return tp.getIncomingKeepAliveTime(); + } + + public void setIncomingKeepAliveTime(long time) { + tp.setIncomingKeepAliveTime(time); + } + + public long getIncomingMessages() { + return tp.getIncomingMessages(); + } + + public int getIncomingQueueSize() { + return tp.getIncomingQueueSize(); + } + + public int getIncomingMaxQueueSize() { + return tp.getIncomingMaxQueueSize(); + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/TPMBean.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/TPMBean.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/TPMBean.java 17 Aug 2012 14:50:59 -0000 1.1 @@ -0,0 +1,60 @@ +package org.jgroups.jmx.protocols; + +import org.jgroups.jmx.ProtocolMBean; +import org.jgroups.Address; + +import java.net.UnknownHostException; +import java.util.List; + +/** + * @author Bela Ban + * @version $Id: TPMBean.java,v 1.1 2012/08/17 14:50:59 marcin Exp $ + */ +public interface TPMBean extends ProtocolMBean { + Address getLocalAddress(); + String getBindAddress(); + String getChannelName(); + long getMessagesSent(); + long getMessagesReceived(); + long getBytesSent(); + long getBytesReceived(); + void setBindAddress(String bind_address) throws UnknownHostException; + boolean isReceiveOnAllInterfaces(); + List getReceiveInterfaces(); + boolean isSendOnAllInterfaces(); + List getSendInterfaces(); + boolean isDiscardIncompatiblePackets(); + void setDiscardIncompatiblePackets(boolean flag); + boolean isEnableBundling(); + void setEnableBundling(boolean flag); + int getMaxBundleSize(); + void setMaxBundleSize(int size); + long getMaxBundleTimeout(); + void setMaxBundleTimeout(long timeout); + boolean isLoopback(); + void setLoopback(boolean b); + boolean isUseIncomingPacketHandler(); + + + int getOOBMinPoolSize(); + void setOOBMinPoolSize(int size); + int getOOBMaxPoolSize(); + void setOOBMaxPoolSize(int size); + int getOOBPoolSize(); + long getOOBKeepAliveTime(); + void setOOBKeepAliveTime(long time); + long getOOBMessages(); + int getOOBQueueSize(); + int getOOBMaxQueueSize(); + + int getIncomingMinPoolSize(); + void setIncomingMinPoolSize(int size); + int getIncomingMaxPoolSize(); + void setIncomingMaxPoolSize(int size); + int getIncomingPoolSize(); + long getIncomingKeepAliveTime(); + void setIncomingKeepAliveTime(long time); + long getIncomingMessages(); + int getIncomingQueueSize(); + int getIncomingMaxQueueSize(); +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/UDP.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/UDP.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/UDP.java 17 Aug 2012 14:51:00 -0000 1.1 @@ -0,0 +1,24 @@ +package org.jgroups.jmx.protocols; + +import org.jgroups.stack.Protocol; + +/** + * @author Bela Ban + * @version $Id: UDP.java,v 1.1 2012/08/17 14:51:00 marcin Exp $ + */ +public class UDP extends org.jgroups.jmx.protocols.TP implements UDPMBean { + org.jgroups.protocols.UDP udp; + + public UDP() { + } + + public UDP(Protocol p) { + super(p); + udp=(org.jgroups.protocols.UDP)p; + } + + public void attachProtocol(Protocol p) { + super.attachProtocol(p); + udp=(org.jgroups.protocols.UDP)p; + } +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/UDPMBean.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/UDPMBean.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/UDPMBean.java 17 Aug 2012 14:50:59 -0000 1.1 @@ -0,0 +1,8 @@ +package org.jgroups.jmx.protocols; + +/** + * @author Bela Ban + * @version $Id: UDPMBean.java,v 1.1 2012/08/17 14:50:59 marcin Exp $ + */ +public interface UDPMBean extends TPMBean { +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/UNICAST.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/UNICAST.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/UNICAST.java 17 Aug 2012 14:50:59 -0000 1.1 @@ -0,0 +1,78 @@ +package org.jgroups.jmx.protocols; + +import org.jgroups.jmx.Protocol; + +/** + * @author Bela Ban + * @version $Id: UNICAST.java,v 1.1 2012/08/17 14:50:59 marcin Exp $ + */ +public class UNICAST extends Protocol implements UNICASTMBean { + org.jgroups.protocols.UNICAST p; + + public UNICAST() { + } + + public UNICAST(org.jgroups.stack.Protocol p) { + super(p); + this.p=(org.jgroups.protocols.UNICAST)p; + } + + public void attachProtocol(org.jgroups.stack.Protocol p) { + super.attachProtocol(p); + this.p=(org.jgroups.protocols.UNICAST)p; + } + + public String getLocalAddress() { + return p.getLocalAddress(); + } + + public String getMembers() { + return p.getMembers(); + } + + + public String printConnections() { + return p.printConnections(); + } + + public long getMessagesSent() { + return p.getNumMessagesSent(); + } + + public long getMessagesReceived() { + return p.getNumMessagesReceived(); + } + + public long getBytesSent() { + return p.getNumBytesSent(); + } + + public long getBytesReceived() { + return p.getNumBytesReceived(); + } + + public long getAcksSent() { + return p.getNumAcksSent(); + } + + public long getAcksReceived() { + return p.getNumAcksReceived(); + } + + public long getXmitRequestsReceived() { + return p.getNumberOfRetransmitRequestsReceived(); + } + + public int getNumUnackedMessages() { + return p.getNumberOfUnackedMessages(); + } + + public String getUnackedMessages() { + return p.getUnackedMessages(); +} + + public int getNumberOfMessagesInReceiveWindows() { + return p.getNumberOfMessagesInReceiveWindows(); + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/UNICASTMBean.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/UNICASTMBean.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/UNICASTMBean.java 17 Aug 2012 14:50:59 -0000 1.1 @@ -0,0 +1,23 @@ +package org.jgroups.jmx.protocols; + +import org.jgroups.jmx.ProtocolMBean; + +/** + * @author Bela Ban + * @version $Id: UNICASTMBean.java,v 1.1 2012/08/17 14:50:59 marcin Exp $ + */ +public interface UNICASTMBean extends ProtocolMBean { + String getLocalAddress(); + String getMembers(); + String printConnections(); + long getMessagesSent(); + long getMessagesReceived(); + long getBytesSent(); + long getBytesReceived(); + long getAcksSent(); + long getAcksReceived(); + long getXmitRequestsReceived(); + int getNumUnackedMessages(); + String getUnackedMessages(); + int getNumberOfMessagesInReceiveWindows(); +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/VIEW_SYNC.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/VIEW_SYNC.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/VIEW_SYNC.java 17 Aug 2012 14:51:00 -0000 1.1 @@ -0,0 +1,73 @@ +package org.jgroups.jmx.protocols; + +import org.jgroups.jmx.Protocol; + +/** + * @author Bela Ban + * @version $Id: VIEW_SYNC.java,v 1.1 2012/08/17 14:51:00 marcin Exp $ + */ +public class VIEW_SYNC extends Protocol implements VIEW_SYNCMBean { + org.jgroups.protocols.VIEW_SYNC p; + + public VIEW_SYNC() { + } + + public VIEW_SYNC(org.jgroups.stack.Protocol p) { + super(p); + this.p=(org.jgroups.protocols.VIEW_SYNC)p; + } + + public void attachProtocol(org.jgroups.stack.Protocol p) { + super.attachProtocol(p); + this.p=(org.jgroups.protocols.VIEW_SYNC)p; + } + + public long getAverageSendInterval() { + return p.getAverageSendInterval(); + } + + public void setAverageSendInterval(long send_interval) { + p.setAverageSendInterval(send_interval); + } + + public int getNumViewsSent() { + return p.getNumViewsSent(); + } + + public int getNumViewsAdjusted() { + return p.getNumViewsAdjusted(); + } + + public long getLastViewRequestSent() { + return p.getLastViewRequestSent(); + } + + public int getNumViewRequestsSent() { + return p.getNumViewRequestsSent(); + } + + public int getNumViewResponsesSeen() { + return p.getNumViewRequestsSent(); + } + + public int getNumViewsLess() { + return p.getNumViewsLess(); + } + + public int getNumViewsEqual() { + return p.getNumViewsEqual(); + } + + public int getNumViewsNonLocal() { + return p.getNumViewsNonLocal(); + } + + public void sendViewRequest() { + p.sendViewRequest(); + } + +// public void sendFakeViewForTestingOnly() { +// p.sendFakeViewForTestingOnly(); +// } + +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/VIEW_SYNCMBean.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/VIEW_SYNCMBean.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/VIEW_SYNCMBean.java 17 Aug 2012 14:50:59 -0000 1.1 @@ -0,0 +1,22 @@ +package org.jgroups.jmx.protocols; + +import org.jgroups.jmx.ProtocolMBean; + +/** + * @author Bela Ban + * @version $Id: VIEW_SYNCMBean.java,v 1.1 2012/08/17 14:50:59 marcin Exp $ + */ +public interface VIEW_SYNCMBean extends ProtocolMBean { + long getAverageSendInterval(); + void setAverageSendInterval(long send_interval); + int getNumViewsSent(); + int getNumViewRequestsSent(); + int getNumViewResponsesSeen(); + int getNumViewsNonLocal(); + int getNumViewsEqual(); + int getNumViewsLess(); + long getLastViewRequestSent(); + int getNumViewsAdjusted(); + void sendViewRequest(); + // void sendFakeViewForTestingOnly(); +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/package.html =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/package.html,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/package.html 17 Aug 2012 14:50:58 -0000 1.1 @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/pbcast/FLUSH.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/pbcast/FLUSH.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/pbcast/FLUSH.java 17 Aug 2012 14:50:55 -0000 1.1 @@ -0,0 +1,43 @@ +package org.jgroups.jmx.protocols.pbcast; + +import org.jgroups.jmx.Protocol; + +/** + * @author Vladimir Blagojevic + * @version $Id: FLUSH.java,v 1.1 2012/08/17 14:50:55 marcin Exp $ + */ +public class FLUSH extends Protocol implements FLUSHMBean { + org.jgroups.protocols.pbcast.FLUSH p; + + public FLUSH(){} + + public FLUSH(org.jgroups.stack.Protocol p){ + super(p); + this.p = (org.jgroups.protocols.pbcast.FLUSH) p; + } + + public void attachProtocol(org.jgroups.stack.Protocol p) { + super.attachProtocol(p); + this.p = (org.jgroups.protocols.pbcast.FLUSH) p; + } + + public double getAverageFlushDuration() { + return p.getAverageFlushDuration(); + } + + public long getTotalTimeInFlush() { + return p.getTotalTimeInFlush(); + } + + public int getNumberOfFlushes() { + return p.getNumberOfFlushes(); + } + + public boolean startFlush() { + return p.startFlush(); + } + + public void stopFlush() { + p.stopFlush(); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/pbcast/FLUSHMBean.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/pbcast/FLUSHMBean.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/pbcast/FLUSHMBean.java 17 Aug 2012 14:50:54 -0000 1.1 @@ -0,0 +1,20 @@ +package org.jgroups.jmx.protocols.pbcast; + +import org.jgroups.jmx.ProtocolMBean; + +/** + * @author Vladimir Blagojevic + * @version $Id: FLUSHMBean.java,v 1.1 2012/08/17 14:50:54 marcin Exp $ + */ +public interface FLUSHMBean extends ProtocolMBean { + + public double getAverageFlushDuration(); + + public long getTotalTimeInFlush(); + + public int getNumberOfFlushes(); + + boolean startFlush(); + + void stopFlush(); +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/pbcast/GMS.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/pbcast/GMS.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/pbcast/GMS.java 17 Aug 2012 14:50:55 -0000 1.1 @@ -0,0 +1,105 @@ +package org.jgroups.jmx.protocols.pbcast; + +import org.jgroups.jmx.Protocol; + +/** + * @author Bela Ban + * @version $Id: GMS.java,v 1.1 2012/08/17 14:50:55 marcin Exp $ + */ +public class GMS extends Protocol implements GMSMBean { + org.jgroups.protocols.pbcast.GMS p; + + public GMS() { + } + + public GMS(org.jgroups.stack.Protocol p) { + super(p); + this.p=(org.jgroups.protocols.pbcast.GMS)p; + } + + public void attachProtocol(org.jgroups.stack.Protocol p) { + super.attachProtocol(p); + this.p=(org.jgroups.protocols.pbcast.GMS)p; + } + + public String getView() { + return p.getView(); + } + + public String getLocalAddress() { + return p.getLocalAddress(); + } + + public String getMembers() { + return p.getMembers(); + } + + public int getNumMembers() { + return p.getNumMembers(); + } + + public boolean isCoordinator() { + return p.isCoordinator(); + } + + public int getNumberOfViews() { + return p.getNumberOfViews(); + } + + public long getJoinTimeout() { + return p.getJoinTimeout(); + } + + public void setJoinTimeout(long t) { + p.setJoinTimeout(t); + } + + public long getJoinRetryTimeout() { + return p.getJoinRetryTimeout(); + } + + public void setJoinRetryTimeout(long t) { + p.setJoinRetryTimeout(t); + } + + public boolean isShun() { + return p.isShun(); + } + + public void setShun(boolean s) { + p.setShun(s); + } + + public String printPreviousMembers() { + return p.printPreviousMembers(); + } + + public String printPreviousViews() { + return p.printPreviousViews(); + } + + public int getViewHandlerQueue() { + return p.viewHandlerSize(); + } + + public boolean isViewHandlerSuspended() { + return p.isViewHandlerSuspended(); + } + + public String dumpViewHandlerQueue() { + return p.dumpViewHandlerQueue(); + } + + public String dumpHistory() { + return p.dumpViewHandlerHistory(); + } + + public void suspendViewHandler() { + p.suspendViewHandler(); + } + + public void resumeViewHandler() { + p.resumeViewHandler(); + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/pbcast/GMSMBean.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/pbcast/GMSMBean.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/pbcast/GMSMBean.java 17 Aug 2012 14:50:54 -0000 1.1 @@ -0,0 +1,30 @@ +package org.jgroups.jmx.protocols.pbcast; + +import org.jgroups.jmx.ProtocolMBean; + +/** + * @author Bela Ban + * @version $Id: GMSMBean.java,v 1.1 2012/08/17 14:50:54 marcin Exp $ + */ +public interface GMSMBean extends ProtocolMBean { + String getView(); + String getLocalAddress(); + String getMembers(); + int getNumMembers(); + boolean isCoordinator(); + int getNumberOfViews(); + long getJoinTimeout(); + void setJoinTimeout(long t); + long getJoinRetryTimeout(); + void setJoinRetryTimeout(long t); + boolean isShun(); + void setShun(boolean s); + String printPreviousMembers(); + String printPreviousViews(); + int getViewHandlerQueue(); + boolean isViewHandlerSuspended(); + String dumpViewHandlerQueue(); + String dumpHistory(); + void suspendViewHandler(); + void resumeViewHandler(); +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/pbcast/NAKACK.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/pbcast/NAKACK.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/pbcast/NAKACK.java 17 Aug 2012 14:50:55 -0000 1.1 @@ -0,0 +1,156 @@ +package org.jgroups.jmx.protocols.pbcast; + +import org.jgroups.jmx.Protocol; + +/** + * @author Bela Ban + * @version $Id: NAKACK.java,v 1.1 2012/08/17 14:50:55 marcin Exp $ + */ +public class NAKACK extends Protocol implements NAKACKMBean { + org.jgroups.protocols.pbcast.NAKACK p; + + public NAKACK() { + } + + public NAKACK(org.jgroups.stack.Protocol p) { + super(p); + this.p=(org.jgroups.protocols.pbcast.NAKACK)p; + } + + public void attachProtocol(org.jgroups.stack.Protocol p) { + super.attachProtocol(p); + this.p=(org.jgroups.protocols.pbcast.NAKACK)p; + } + + public int getGcLag() { + return p.getGcLag(); + } + + public void setGcLag(int gc_lag) { + p.setGcLag(gc_lag); + } + + public boolean isUseMcastXmit() { + return p.isUseMcastXmit(); + } + + public void setUseMcastXmit(boolean use_mcast_xmit) { + p.setUseMcastXmit(use_mcast_xmit); + } + + public boolean isXmitFromRandomMember() { + return p.isXmitFromRandomMember(); + } + + public void setXmitFromRandomMember(boolean xmit_from_random_member) { + p.setXmitFromRandomMember(xmit_from_random_member); + } + + public boolean isDiscardDeliveredMsgs() { + return p.isDiscardDeliveredMsgs(); + } + + public void setDiscardDeliveredMsgs(boolean discard_delivered_msgs) { + p.setDiscardDeliveredMsgs(discard_delivered_msgs); + } + + public int getMaxXmitBufSize() { + return p.getMaxXmitBufSize(); + } + + public void setMaxXmitBufSize(int max_xmit_buf_size) { + p.setMaxXmitBufSize(max_xmit_buf_size); + } + + /** + * + * @return + * @deprecated removed in 2.6 + */ + public long getMaxXmitSize() { + return -1; + } + + /** + * @param max_xmit_size + * @deprecated removed in 2.6 + */ + public void setMaxXmitSize(long max_xmit_size) { + } + + public long getXmitRequestsReceived() { + return p.getXmitRequestsReceived(); + } + + public long getXmitRequestsSent() { + return p.getXmitRequestsSent(); + } + + public long getXmitResponsesReceived() { + return p.getXmitResponsesReceived(); + } + + public long getXmitResponsesSent() { + return p.getXmitResponsesSent(); + } + + public long getMissingMessagesReceived() { + return p.getMissingMessagesReceived(); + } + + public int getPendingRetransmissionRequests() { + return p.getPendingRetransmissionRequests(); + } + + public int getXmitTableSize() { + return p.getXmitTableSize(); + } + + public String printXmitTable() { + return p.printMessages(); + } + + public String printMessages() { + return p.printMessages(); + } + + public String printStabilityMessages() { + return p.printStabilityMessages(); + } + + public String printMergeHistory() { + return p.printMergeHistory(); + } + + public String printRetransmissionAvgs() { + return p.printRetransmissionAvgs(); + } + + public String printRetransmissionTimes() { + return p.printRetransmissionTimes(); + } + + public String printSmoothedRetransmissionAvgs() { + return p.printSmoothedRetransmissionAvgs(); + } + + public String printLossRates() { + return p.printLossRates(); + } + + public double getTotalAvgXmitTime() { + return p.getTotalAverageRetransmissionTime(); + } + + public double getTotalAvgSmoothedXmitTime() { + return p.getTotalAverageSmoothedRetransmissionTime(); + } + + public int getAverageLossRate() { + return (int)(p.getAverageLossRate() * 100); + } + + public int getAverageSmoothedLossRate() { + return (int)(p.getAverageSmoothedLossRate() * 100); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/pbcast/NAKACKMBean.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/pbcast/NAKACKMBean.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/pbcast/NAKACKMBean.java 17 Aug 2012 14:50:54 -0000 1.1 @@ -0,0 +1,53 @@ +package org.jgroups.jmx.protocols.pbcast; + +import org.jgroups.jmx.ProtocolMBean; + +/** + * @author Bela Ban + * @version $Id: NAKACKMBean.java,v 1.1 2012/08/17 14:50:54 marcin Exp $ + */ +public interface NAKACKMBean extends ProtocolMBean { + int getGcLag(); + void setGcLag(int gc_lag); + boolean isUseMcastXmit(); + void setUseMcastXmit(boolean use_mcast_xmit); + boolean isXmitFromRandomMember(); + void setXmitFromRandomMember(boolean xmit_from_random_member); + boolean isDiscardDeliveredMsgs(); + void setDiscardDeliveredMsgs(boolean discard_delivered_msgs); + int getMaxXmitBufSize(); + void setMaxXmitBufSize(int max_xmit_buf_size); + + /** + * + * @return + * @deprecated removed in 2.6 + */ + long getMaxXmitSize(); + + /** + * + * @param max_xmit_size + * @deprecated removed in 2.6 + */ + void setMaxXmitSize(long max_xmit_size); + int getXmitTableSize(); + long getXmitRequestsReceived(); + long getXmitRequestsSent(); + long getXmitResponsesReceived(); + long getXmitResponsesSent(); + long getMissingMessagesReceived(); + int getPendingRetransmissionRequests(); + String printXmitTable(); + String printMessages(); + String printStabilityMessages(); + String printMergeHistory(); + String printRetransmissionAvgs(); + String printRetransmissionTimes(); + String printSmoothedRetransmissionAvgs(); + String printLossRates(); + double getTotalAvgXmitTime(); + double getTotalAvgSmoothedXmitTime(); + int getAverageLossRate(); + int getAverageSmoothedLossRate(); +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/pbcast/STABLE.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/pbcast/STABLE.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/pbcast/STABLE.java 17 Aug 2012 14:50:55 -0000 1.1 @@ -0,0 +1,65 @@ +package org.jgroups.jmx.protocols.pbcast; + +import org.jgroups.jmx.Protocol; + +/** + * @author Bela Ban + * @version $Id: STABLE.java,v 1.1 2012/08/17 14:50:55 marcin Exp $ + */ +public class STABLE extends Protocol implements STABLEMBean { + org.jgroups.protocols.pbcast.STABLE p; + + public STABLE() { + } + + public STABLE(org.jgroups.stack.Protocol p) { + super(p); + this.p=(org.jgroups.protocols.pbcast.STABLE)p; + } + + public void attachProtocol(org.jgroups.stack.Protocol p) { + super.attachProtocol(p); + this.p=(org.jgroups.protocols.pbcast.STABLE)p; + } + + public long getDesiredAverageGossip() { + return p.getDesiredAverageGossip(); + } + + public void setDesiredAverageGossip(long gossip_interval) { + p.setDesiredAverageGossip(gossip_interval); + } + + public long getMaxBytes() { + return p.getMaxBytes(); + } + + public void setMaxBytes(long max_bytes) { + p.setMaxBytes(max_bytes); + } + + public long getBytes() { + return p.getBytes(); + } + + public int getStableSent() { + return p.getStableSent(); + } + + public int getStableReceived() { + return p.getStableReceived(); + } + + public int getStabilitySent() { + return p.getStabilitySent(); + } + + public int getStabilityReceived() { + return p.getStabilityReceived(); + } + + public void runMessageGarbageCollection() { + p.runMessageGarbageCollection(); + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/pbcast/STABLEMBean.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/pbcast/STABLEMBean.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/pbcast/STABLEMBean.java 17 Aug 2012 14:50:54 -0000 1.1 @@ -0,0 +1,20 @@ +package org.jgroups.jmx.protocols.pbcast; + +import org.jgroups.jmx.ProtocolMBean; + +/** + * @author Bela Ban + * @version $Id: STABLEMBean.java,v 1.1 2012/08/17 14:50:54 marcin Exp $ + */ +public interface STABLEMBean extends ProtocolMBean { + long getDesiredAverageGossip(); + void setDesiredAverageGossip(long gossip_interval); + long getMaxBytes(); + void setMaxBytes(long max_bytes); + long getBytes(); + int getStableSent(); + int getStableReceived(); + int getStabilitySent(); + int getStabilityReceived(); + void runMessageGarbageCollection(); +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/pbcast/STATE_TRANSFER.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/pbcast/STATE_TRANSFER.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/pbcast/STATE_TRANSFER.java 17 Aug 2012 14:50:54 -0000 1.1 @@ -0,0 +1,36 @@ +package org.jgroups.jmx.protocols.pbcast; + +import org.jgroups.jmx.Protocol; + +/** + * @author Bela Ban + * @version $Id: STATE_TRANSFER.java,v 1.1 2012/08/17 14:50:54 marcin Exp $ + */ +public class STATE_TRANSFER extends Protocol implements STATE_TRANSFERMBean { + org.jgroups.protocols.pbcast.STATE_TRANSFER p; + + public STATE_TRANSFER() { + } + + public STATE_TRANSFER(org.jgroups.stack.Protocol p) { + super(p); + this.p=(org.jgroups.protocols.pbcast.STATE_TRANSFER)p; + } + + public void attachProtocol(org.jgroups.stack.Protocol p) { + super.attachProtocol(p); + this.p=(org.jgroups.protocols.pbcast.STATE_TRANSFER)p; + } + + public int getNumberOfStateRequests() { + return p.getNumberOfStateRequests(); + } + + public long getNumberOfStateBytesSent() { + return p.getNumberOfStateBytesSent(); + } + + public double getAverageStateSize() { + return p.getAverageStateSize(); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/pbcast/STATE_TRANSFERMBean.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/pbcast/STATE_TRANSFERMBean.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/pbcast/STATE_TRANSFERMBean.java 17 Aug 2012 14:50:54 -0000 1.1 @@ -0,0 +1,13 @@ +package org.jgroups.jmx.protocols.pbcast; + +import org.jgroups.jmx.ProtocolMBean; + +/** + * @author Bela Ban + * @version $Id: STATE_TRANSFERMBean.java,v 1.1 2012/08/17 14:50:54 marcin Exp $ + */ +public interface STATE_TRANSFERMBean extends ProtocolMBean { + int getNumberOfStateRequests(); + long getNumberOfStateBytesSent(); + double getAverageStateSize(); +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/pbcast/STREAMING_STATE_TRANSFER.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/pbcast/STREAMING_STATE_TRANSFER.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/pbcast/STREAMING_STATE_TRANSFER.java 17 Aug 2012 14:50:54 -0000 1.1 @@ -0,0 +1,36 @@ +package org.jgroups.jmx.protocols.pbcast; + +import org.jgroups.jmx.Protocol; + +/** + * @author Vladimir Blagojevic + * @version $Id: STREAMING_STATE_TRANSFER.java,v 1.1 2012/08/17 14:50:54 marcin Exp $ + */ +public class STREAMING_STATE_TRANSFER extends Protocol implements STREAMING_STATE_TRANSFERMBean { + org.jgroups.protocols.pbcast.STREAMING_STATE_TRANSFER p; + + public STREAMING_STATE_TRANSFER() { + } + + public STREAMING_STATE_TRANSFER(org.jgroups.stack.Protocol p) { + super(p); + this.p=(org.jgroups.protocols.pbcast.STREAMING_STATE_TRANSFER)p; + } + + public void attachProtocol(org.jgroups.stack.Protocol p) { + super.attachProtocol(p); + this.p=(org.jgroups.protocols.pbcast.STREAMING_STATE_TRANSFER)p; + } + + public int getNumberOfStateRequests() { + return p.getNumberOfStateRequests(); + } + + public long getNumberOfStateBytesSent() { + return p.getNumberOfStateBytesSent(); + } + + public double getAverageStateSize() { + return p.getAverageStateSize(); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/pbcast/STREAMING_STATE_TRANSFERMBean.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/pbcast/STREAMING_STATE_TRANSFERMBean.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/pbcast/STREAMING_STATE_TRANSFERMBean.java 17 Aug 2012 14:50:55 -0000 1.1 @@ -0,0 +1,13 @@ +package org.jgroups.jmx.protocols.pbcast; + +import org.jgroups.jmx.ProtocolMBean; + +/** + * @author Vladimir Blagojevic + * @version $Id: STREAMING_STATE_TRANSFERMBean.java,v 1.1 2012/08/17 14:50:55 marcin Exp $ + */ +public interface STREAMING_STATE_TRANSFERMBean extends ProtocolMBean { + int getNumberOfStateRequests(); + long getNumberOfStateBytesSent(); + double getAverageStateSize(); +} Index: 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/pbcast/package.html =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/jmx/protocols/pbcast/package.html,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/jmx/protocols/pbcast/package.html 17 Aug 2012 14:50:55 -0000 1.1 @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file Index: 3rdParty_sources/jgroups/org/jgroups/mux/Multiplexer.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/mux/Multiplexer.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/mux/Multiplexer.java 17 Aug 2012 14:51:28 -0000 1.1 @@ -0,0 +1,1219 @@ +package org.jgroups.mux; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.*; +import org.jgroups.TimeoutException; +import org.jgroups.protocols.pbcast.FLUSH; +import org.jgroups.stack.ProtocolStack; +import org.jgroups.stack.StateTransferInfo; +import org.jgroups.util.*; +import org.jgroups.util.ThreadFactory; + +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * The multiplexer allows multiple channel interfaces to be associated with one + * underlying instance of JChannel. + * + *

+ * The multiplexer is essentially a building block residing on top of a JChannel + * providing multiplexing functionality to N instances of MuxChannel. Since + * MuxChannel extends the JGroups JChannel class, user applications are + * completely unaware of this change in the underlying plumbing. + * + *

+ * Each JGroups application sharing a channel through a multiplexer has to + * create a MuxChannel with a unique application id. The multiplexer keeps track + * of all registered applications and tags messages belonging to a specific + * application with that id for sent messages. When receiving a message from a + * remote peer, the multiplexer will dispatch a message to the appropriate + * MuxChannel depending on the id attached to the message. + * + * + * @author Bela Ban, Vladimir Blagojevic + * @see MuxChannel + * @see Channel + * @version $Id: Multiplexer.java,v 1.1 2012/08/17 14:51:28 marcin Exp $ + */ +public class Multiplexer implements UpHandler { + + private static final Log log=LogFactory.getLog(Multiplexer.class); + private static final String SEPARATOR="::"; + private static final short SEPARATOR_LEN=(short)SEPARATOR.length(); + private static final String NAME="MUX"; + + /** + * Map. Maintains the mapping between service IDs and + * their associated MuxChannels + */ + private final ConcurrentMap services=new ConcurrentHashMap(); + private final JChannel channel; + + /** Thread pool to concurrently process messages sent to different services */ + private final ExecutorService thread_pool; + + /** + * To make sure messages sent to different services are processed + * concurrently (using the thread pool above), but messages to the same + * service are processed FIFO + */ + private final FIFOMessageQueue fifo_queue=new FIFOMessageQueue(); + + /** To collect service acks from Multiplexers */ + private final AckCollector service_ack_collector=new AckCollector(); + + protected long service_ack_timeout = 2000; + + /** Cluster view */ + private volatile View view=null; + + /** + * Map. Map of service IDs and booleans that determine + * whether getState() has already been called + */ + private final Map state_transfer_listeners=new HashMap(); + + /** + * Map>. A map of services as keys and lists of hosts + * as values + */ + private final Map> service_state=new HashMap>(); + + /** + * Map>. Keys are senders, values are a set of + * services hosted by that sender. Used to collect responses to + * LIST_SERVICES_REQ + */ + private final Map> service_responses=new HashMap>(); + + private final List

services_merged_collector=new ArrayList
(); + + private AtomicBoolean services_merged=new AtomicBoolean(false); + + private long service_response_timeout=3000; + + public Multiplexer(JChannel channel) { + if(channel == null || !channel.isOpen()) + throw new IllegalArgumentException("Channel " + channel + + " cannot be used for Multiplexer"); + + this.channel=channel; + this.channel.addChannelListener(new MultiplexerChannelListener()); + this.channel.setUpHandler(this); + this.channel.setOpt(Channel.BLOCK, Boolean.TRUE); // we want to handle BLOCK events ourselves + + //thread pool is enabled by default + boolean use_thread_pool=Global.getPropertyAsBoolean(Global.MUX_ENABLED, true); + if(use_thread_pool) { + thread_pool=createThreadPool(); + } + else { + thread_pool=null; + } + } + + JChannel getChannel() { + return channel; + } + + /** + * @deprecated Use ${link #getServiceIds()} instead + * @return The set of service IDs + */ + public Set getApplicationIds() { + return getServiceIds(); + } + + public Set getServiceIds() { + return Collections.unmodifiableSet(services.keySet()); + } + + public long getServicesResponseTimeout() { + return service_response_timeout; + } + + public void setServicesResponseTimeout(long services_rsp_timeout) { + this.service_response_timeout=services_rsp_timeout; + } + + + public long getServiceAckTimeout() { + return service_ack_timeout; + } + + public void setServiceAckTimeout(long service_ack_timeout) { + this.service_ack_timeout=service_ack_timeout; + } + + /** + * Returns a copy of the current view minus the nodes on which + * service service_id is not running + * + * @param service_id + * @return The service view + */ + View getServiceView(String service_id) { + List
hosts=service_state.get(service_id); + if(hosts == null) + return null; + return generateServiceView(hosts); + } + + boolean stateTransferListenersPresent() { + return !state_transfer_listeners.isEmpty(); + } + + public synchronized void registerForStateTransfer(String appl_id, String substate_id) { + String key=appl_id; + if(substate_id != null && substate_id.length() > 0) + key+=SEPARATOR + substate_id; + state_transfer_listeners.put(key, Boolean.FALSE); + } + + synchronized boolean getState(Address target, String id, long timeout) throws ChannelNotConnectedException, + ChannelClosedException { + if(state_transfer_listeners.isEmpty()) + return false; + + for(Iterator> it=state_transfer_listeners.entrySet().iterator();it.hasNext();) { + Map.Entry entry=it.next(); + String key=entry.getKey(); + int index=key.indexOf(SEPARATOR); + boolean match; + if(index > -1) { + String tmp=key.substring(0, index); + match=id.equals(tmp); + } + else { + match=id.equals(key); + } + if(match) { + entry.setValue(Boolean.TRUE); + break; + } + } + + Collection values=state_transfer_listeners.values(); + boolean all_true=Util.all(values, Boolean.TRUE); + if(!all_true) + return true; // pseudo + + boolean rc=false; + Set keys=new HashSet(state_transfer_listeners.keySet()); + rc=fetchServiceStates(target, keys, timeout); + state_transfer_listeners.clear(); + return rc; + } + + protected ThreadPoolExecutor createThreadPool() { + int min_threads=1, max_threads=4; + long keep_alive=30000; + + Map m=channel.getInfo(); + min_threads=Global.getPropertyAsInteger(Global.MUX_MIN_THREADS, min_threads); + max_threads=Global.getPropertyAsInteger(Global.MUX_MAX_THREADS, max_threads); + keep_alive=Global.getPropertyAsLong(Global.MUX_KEEPALIVE, keep_alive); + + ThreadFactory factory=new DefaultThreadFactory(Util.getGlobalThreadGroup(), "Multiplexer", false, true); + return new ThreadPoolExecutor(min_threads, max_threads, keep_alive, TimeUnit.MILLISECONDS, + new SynchronousQueue(), + factory, + new ShutdownRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy())); + } + + protected void shutdownThreadPool() { + if(thread_pool != null && !thread_pool.isShutdown()) { + thread_pool.shutdownNow(); + try { + thread_pool.awaitTermination(Global.THREADPOOL_SHUTDOWN_WAIT_TIME, + TimeUnit.MILLISECONDS); + } + catch(InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } + + /** + * Fetches the app states for all service IDs in keys. The keys are a + * duplicate list, so it cannot be modified by the caller of this method + * + * @param keys + */ + private boolean fetchServiceStates(Address target, Set keys, long timeout) throws ChannelClosedException, + ChannelNotConnectedException { + boolean rc, all_tranfers_ok=false; + boolean flushStarted=Util.startFlush(channel); + if(flushStarted) { + try { + for(String stateId:keys) { + rc=channel.getState(target, stateId, timeout, false); + if(!rc) + throw new Exception("Failed transfer for state id " + stateId + + ", state provider was " + + target); + } + all_tranfers_ok=true; + } + catch(Exception e) { + log.warn("Failed multiple state transfer under one flush phase ", e); + } + finally { + channel.stopFlush(); + } + } + return flushStarted && all_tranfers_ok; + } + + void sendServiceUpMessage(String service) throws Exception { + // we have to make this service message non OOB since we have + // to FIFO order service messages and BLOCK/UNBLOCK messages + sendServiceMessage(true, ServiceInfo.SERVICE_UP, service, null, false); + } + + void sendServiceDownMessage(String service) throws Exception { + // we have to make this service message non OOB since we have + // to FIFO order service messages and BLOCK/UNBLOCK messages + sendServiceMessage(true, ServiceInfo.SERVICE_DOWN, service, null, false); + } + + /** + * Remove mux header and dispatch to correct MuxChannel + * + * @param evt + * @return + */ + public Object up(final Event evt) { + switch(evt.getType()) { + case Event.MSG: + final Message msg=(Message)evt.getArg(); + final MuxHeader hdr=(MuxHeader)msg.getHeader(NAME); + if(hdr == null) { + log.error("MuxHeader not present - discarding message " + msg); + return null; + } + + Address sender=msg.getSrc(); + boolean isServiceMessage=hdr.info != null; + if(isServiceMessage) { + try { + handleServiceMessage(hdr.info, sender); + } + catch(Exception e) { + if(log.isErrorEnabled()) + log.error("failure in handling service message " + hdr.info + + " from sender " + + sender, e); + } + return null; + } + else { + //regular message between MuxChannel(s) + final MuxChannel mux_ch=services.get(hdr.id); + return mux_ch == null? null : passToMuxChannel(mux_ch, + evt, + fifo_queue, + sender, + hdr.id, + false, + msg.isFlagSet(Message.OOB)); + } + + case Event.VIEW_CHANGE: + Vector
old_members=view != null? view.getMembers() : null; + view=(View)evt.getArg(); + Vector
new_members=view != null? view.getMembers() : null; + Vector
left_members=Util.determineLeftMembers(old_members, new_members); + + if(view instanceof MergeView) { + final MergeView temp_merge_view=(MergeView)view.clone(); + if(log.isTraceEnabled()) + log.trace("received a MergeView: " + temp_merge_view + + ", adjusting the service view"); + try { + handleMergeView(temp_merge_view); + } + catch(Exception e) { + if(log.isErrorEnabled()) + log.error("failed handling merge view", e); + } + finally { + synchronized(service_responses) { + service_responses.clear(); + } + synchronized(services_merged_collector) { + services_merged_collector.clear(); + } + services_merged.set(false); + } + } + else { // regular view + HashMap> payload=(HashMap>)view.getPayload("service_state"); + if(payload != null) { + synchronized(service_state) { + service_state.putAll(payload); + } + } + + } + service_ack_collector.handleView(view); + for(Address member:left_members) { + try { + adjustServiceView(member); + } + catch(Throwable t) { + if(log.isErrorEnabled()) + log.error("failed adjusting service views", t); + } + } + break; + + case Event.PREPARE_VIEW: + View prepare_view=(View)evt.getArg(); + old_members=view != null? view.getMembers() : new Vector
(); + + Vector
added_members=new Vector
(prepare_view.getMembers()); + added_members.removeAll(old_members); + + if(!added_members.isEmpty()) { + synchronized(service_state) { + prepare_view.addPayload("service_state", service_state); + } + } + break; + + case Event.SUSPECT: + Address suspected_mbr=(Address)evt.getArg(); + service_ack_collector.suspect(suspected_mbr); + /* + * http://jira.jboss.com/jira/browse/JGRP-665 + * Intentionally do not update service_response since we might + * get false suspect while merging if FD is aggressive enough. + * Instead rely on ServiceInfo.SERVICES_MERGED and timeout + * mechanism in handleMergeView + */ + passToAllMuxChannels(evt); + break; + + case Event.GET_APPLSTATE: + return handleStateRequest(evt, true); + case Event.STATE_TRANSFER_OUTPUTSTREAM: + handleStateRequest(evt, true); + break; + case Event.GET_STATE_OK: + case Event.STATE_TRANSFER_INPUTSTREAM: + handleStateResponse(evt, true); + break; + + case Event.SET_LOCAL_ADDRESS: + passToAllMuxChannels(evt); + break; + + case Event.BLOCK: + passToAllMuxChannels(evt, true, true); + return null; + + case Event.UNBLOCK: // process queued-up MergeViews + passToAllMuxChannels(evt); + break; + case Event.EXIT: + //we are being shunned, close all services + closeAll(); + break; + + default: + passToAllMuxChannels(evt); + break; + } + return null; + } + + public Channel createMuxChannel(String id, String stack_name) throws Exception { + if(services.containsKey(id)) { + throw new Exception("service ID \"" + id + + "\" is already registered at channel" + + getLocalAddress() + + ", cannot register service with duplicate ID at the same channel"); + } + MuxChannel ch=new MuxChannel(id, stack_name, this); + services.put(id, ch); + return ch; + } + + private void passToAllMuxChannels(Event evt) { + passToAllMuxChannels(evt, false, true); + } + + private void passToAllMuxChannels(Event evt, boolean block, boolean bypass_thread_pool) { + String service_name; + MuxChannel ch; + for(Map.Entry entry:services.entrySet()) { + service_name=entry.getKey(); + ch=entry.getValue(); + // these events are directly delivered, don't get added to any queue + passToMuxChannel(ch, evt, fifo_queue, null, service_name, block, bypass_thread_pool); + } + } + + void addServiceIfNotPresent(String id, MuxChannel ch) { + services.putIfAbsent(id, ch); + } + + protected MuxChannel removeService(String id) { + MuxChannel ch=services.remove(id); + //http://jira.jboss.com/jira/browse/JGRP-623 + if(ch != null) + ch.up(new Event(Event.UNBLOCK)); + return ch; + } + + /** Closes the underlying JChannel if all MuxChannels have been disconnected */ + void disconnect() { + boolean all_disconnected=true; + for(MuxChannel mux_ch:services.values()) { + if(mux_ch.isConnected()) { + all_disconnected=false; + break; + } + } + if(all_disconnected) { + if(log.isTraceEnabled()) { + log.trace("disconnecting underlying JChannel as all MuxChannels are disconnected"); + } + channel.disconnect(); + } + } + + public boolean close() { + boolean all_closed=true; + for(MuxChannel mux_ch:services.values()) { + if(mux_ch.isOpen()) { + all_closed=false; + break; + } + } + if(all_closed) { + if(log.isTraceEnabled()) { + log.trace("closing underlying JChannel as all MuxChannels are closed"); + } + channel.close(); + services.clear(); + shutdownThreadPool(); + } + return all_closed; + } + + public void closeAll() { + for(MuxChannel mux_ch:services.values()) { + mux_ch.setConnected(false); + mux_ch.setClosed(true); + mux_ch.closeMessageQueue(true); + } + } + + boolean shutdown() { + boolean all_closed=true; + for(MuxChannel mux_ch:services.values()) { + if(mux_ch.isOpen()) { + all_closed=false; + break; + } + } + if(all_closed) { + if(log.isTraceEnabled()) { + log.trace("shutting down underlying JChannel as all MuxChannels are closed"); + } + channel.shutdown(); + services.clear(); + shutdownThreadPool(); + } + return all_closed; + } + + Address getLocalAddress() { + return channel.getLocalAddress(); + } + + boolean flushSupported() { + return channel.flushSupported(); + } + + boolean startFlush(boolean automatic_resume) { + boolean b = Util.startFlush(channel); + if(automatic_resume) + channel.stopFlush(); + return b; + } + + void stopFlush() { + channel.stopFlush(); + } + + boolean isConnected() { + return channel.isConnected(); + } + + void connect(String cluster_name) throws ChannelException { + channel.connect(cluster_name); + } + + /** + * Determines whether the channel is open; i.e., the protocol stack has been + * created (may not be connected though). + */ + boolean isOpen() { + return channel.isOpen(); + } + + void open() throws ChannelException { + channel.open(); + } + + /** + * Returns an Address of a state provider for a given service_id. If + * preferredTarget is a member of a service view for a given service_id then + * preferredTarget is returned. Otherwise, service view coordinator is + * returned if such node exists. If service view is empty for a given + * service_id null is returned. + * + * @param preferredTarget + * @param service_id + * @return + */ + Address getStateProvider(Address preferredTarget, String service_id) { + Address result=null; + List
hosts=service_state.get(service_id); + if(hosts != null && !hosts.isEmpty()) { + if(hosts.contains(preferredTarget)) { + result=preferredTarget; + } + else { + result=hosts.get(0); + } + } + return result; + } + + private void sendServiceMessage(boolean synchronous, + byte type, + String service, + byte[] payload, + boolean oob) throws Exception { + + Address host=getLocalAddress(); + if(host == null) { + if(log.isWarnEnabled()) { + log.warn("local_addr is null, cannot send ServiceInfo." + ServiceInfo.typeToString(type) + + " message"); + } + return; + } + + if(!channel.isOpen() || !channel.isConnected()) { + if(log.isWarnEnabled()) { + log.warn("Underlying multiplexer channel " + channel.getLocalAddress() + + " is not connected, cannot send ServiceInfo." + + ServiceInfo.typeToString(type) + + " message"); + } + return; + } + + Message service_msg=new Message(); + service_msg.putHeader(NAME, new MuxHeader(new ServiceInfo(type, service, host, payload))); + + if(oob) + service_msg.setFlag(Message.OOB); + + if(channel.flushSupported()) + service_msg.putHeader(FLUSH.NAME, new FLUSH.FlushHeader(FLUSH.FlushHeader.FLUSH_BYPASS)); + + if(synchronous) { + //for synchronous invocation we need to collect acks + //the host that is sending this message should also ack + CopyOnWriteArrayList
muxChannels=new CopyOnWriteArrayList
(); + muxChannels.add(host); + List
list=service_state.get(service); + if(list != null && !list.isEmpty()) { + muxChannels.addAllAbsent(list); + } + + //initialize collector and ... + service_ack_collector.reset(null, muxChannels); + int size=service_ack_collector.size(); + + //then send a message + channel.send(service_msg); + long start=System.currentTimeMillis(); + try { + service_ack_collector.waitForAllAcks(service_ack_timeout); + if(log.isTraceEnabled()) + log.trace("received all service ACKs (" + size + + ") in " + + (System.currentTimeMillis() - start) + + "ms"); + } + catch(TimeoutException e) { + log.warn("failed to collect all service ACKs (" + size + + ") for " + + service_msg + + " after " + + service_ack_timeout + + "ms, missing ACKs from " + + service_ack_collector.printMissing() + + " (received=" + + service_ack_collector.printReceived() + + "), local_addr=" + + getLocalAddress()); + } + } + else { + //if asynchronous then fire and forget + channel.send(service_msg); + } + } + + private Object handleStateRequest(Event evt, boolean hasReturnValue) { + StateTransferInfo info=(StateTransferInfo)evt.getArg(); + String id=info.state_id; + String original_id=id; + Address requester=info.target; // the sender of the state request + + if(id == null) { + if(log.isWarnEnabled()) { + log.warn("Invalid state request " + info + " arrived at Multiplexer, dropping it"); + } + return new StateTransferInfo(null, original_id, 0L, null); + } + + try { + int index=id.indexOf(SEPARATOR); + if(index > -1) { + info.state_id=id.substring(index + SEPARATOR_LEN); + id=id.substring(0, index); // similar reuse as above... + } + else { + info.state_id=null; + } + + MuxChannel mux_ch=services.get(id); + //JGRP-616 + if(mux_ch == null) { + if(log.isWarnEnabled()) + log.warn("State provider " + channel.getLocalAddress() + + " does not have service with id " + + id + + ", returning null state"); + + return new StateTransferInfo(null, original_id, 0L, null); + } + + // state_id will be null, get regular state from the service named state_id + StateTransferInfo ret=(StateTransferInfo)passToMuxChannel(mux_ch, + evt, + fifo_queue, + requester, + id, + hasReturnValue); + if(ret != null) { + ret.state_id=original_id; + } + else { + return new StateTransferInfo(null, original_id, 0L, null); + } + return ret; + } + catch(Throwable ex) { + if(log.isErrorEnabled()) + log.error("failed returning the application state, will return null", ex); + return new StateTransferInfo(null, original_id, 0L, null); + } + } + + private void handleStateResponse(Event evt, boolean block) { + StateTransferInfo info=(StateTransferInfo)evt.getArg(); + MuxChannel mux_ch; + Address state_sender=info.target; + + String appl_id, substate_id, tmp; + tmp=info.state_id; + + if(tmp == null) { + if(log.isTraceEnabled()) + log.trace("state is null, not passing up: " + info); + return; + } + + int index=tmp.indexOf(SEPARATOR); + if(index > -1) { + appl_id=tmp.substring(0, index); + substate_id=tmp.substring(index + SEPARATOR_LEN); + } + else { + appl_id=tmp; + substate_id=null; + } + + mux_ch=services.get(appl_id); + if(mux_ch == null) { + log.error("State receiver " + channel.getLocalAddress() + + " does not have service with id " + + appl_id); + } + else { + StateTransferInfo tmp_info=info.copy(); + tmp_info.state_id=substate_id; + Event tmpEvt=new Event(evt.getType(), tmp_info); + passToMuxChannel(mux_ch, tmpEvt, fifo_queue, state_sender, appl_id, block); + } + } + + private void handleServiceMessage(ServiceInfo info, Address sender) throws Exception { + switch(info.type) { + case ServiceInfo.SERVICE_UP: + handleServiceUp(info.service, info.host); + ackServiceMessage(info, sender); + break; + case ServiceInfo.SERVICE_DOWN: + handleServiceDown(info.service, info.host); + ackServiceMessage(info, sender); + break; + case ServiceInfo.LIST_SERVICES_RSP: + handleServicesRsp(sender, info.state); + break; + case ServiceInfo.ACK: + service_ack_collector.ack(sender); + break; + case ServiceInfo.SERVICES_MERGED: + synchronized(services_merged_collector) { + if(!services_merged_collector.contains(sender)) { + services_merged_collector.add(sender); + } + boolean mergeOk=view != null && services_merged_collector.containsAll(view.getMembers()); + services_merged.set(mergeOk); + if(log.isDebugEnabled()) + log.debug(getLocalAddress() + " got service merged from " + + sender + + " merged so far " + + services_merged_collector + + " view is " + + view.size()); + + } + break; + default: + if(log.isErrorEnabled()) + log.error("service request type " + info.type + " not known"); + break; + } + } + + private void ackServiceMessage(ServiceInfo info, Address ackTarget) throws ChannelNotConnectedException, + ChannelClosedException { + + Message ack=new Message(ackTarget, null, null); + ack.setFlag(Message.OOB); + + ServiceInfo si=new ServiceInfo(ServiceInfo.ACK, info.service, info.host, null); + MuxHeader hdr=new MuxHeader(si); + ack.putHeader(NAME, hdr); + + if(channel.isConnected()) + channel.send(ack); + } + + private void handleServicesRsp(Address sender, byte[] state) throws Exception { + Set s=(Set)Util.objectFromByteBuffer(state); + boolean all_merged=false; + + synchronized(service_responses) { + Set tmp=service_responses.get(sender); + if(tmp == null) + tmp=new HashSet(); + tmp.addAll(s); + + service_responses.put(sender, tmp); + if(log.isDebugEnabled()) + log.debug(getLocalAddress() + " received service response: " + + sender + + "(" + + s.toString() + + ")"); + + all_merged=(view != null && service_responses.keySet().containsAll(view.getMembers())); + } + if(all_merged) { + if(log.isDebugEnabled()) + log.debug(getLocalAddress() + " sent service merged " + + service_responses.keySet() + + " view is " + + view.getMembers()); + + sendServiceMessage(false, ServiceInfo.SERVICES_MERGED, null, null, true); + } + } + + private void handleServiceDown(String service, Address host) { + List
hosts, hosts_copy; + boolean removed=false; + + synchronized(service_state) { + hosts=service_state.get(service); + if(hosts == null) + return; + removed=hosts.remove(host); + hosts_copy=new ArrayList
(hosts); // make a copy so we don't modify hosts in generateServiceView() + } + + if(removed) { + View service_view=generateServiceView(hosts_copy); + MuxChannel ch=services.get(service); + if(ch != null && ch.isConnected()) { + Event view_evt=new Event(Event.VIEW_CHANGE, service_view); + //we cannot pass service message to thread pool since we have + //to FIFO order service messages with BLOCK/UNBLOCK messages + //therefore all of them have to bypass thread pool + + passToMuxChannel(ch, view_evt, fifo_queue, null, service, false, true); + } + else { + if(log.isTraceEnabled()) + log.trace("service " + service + + " not found, cannot dispatch service view " + + service_view); + } + } + + Address local_address=getLocalAddress(); + boolean isMyService=local_address != null && local_address.equals(host); + if(isMyService) + removeService(service); + } + + private void handleServiceUp(String service, Address host) { + List
hosts, hosts_copy; + boolean added=false; + + synchronized(service_state) { + hosts=service_state.get(service); + if(hosts == null) { + hosts=new CopyOnWriteArrayList
(); + service_state.put(service, hosts); + } + if(!hosts.contains(host)) { + hosts.add(host); + added=true; + } + hosts_copy=new ArrayList
(hosts); // make a copy so we don't modify hosts in generateServiceView() + } + + if(added) { + View service_view=generateServiceView(hosts_copy); + MuxChannel ch=services.get(service); + if(ch != null) { + Event view_evt=new Event(Event.VIEW_CHANGE, service_view); + //we cannot pass service message to thread pool since we have + //to FIFO order service messages with BLOCK/UNBLOCK messages + //therefore all of them have to bypass thread pool + passToMuxChannel(ch, view_evt, fifo_queue, null, service, false, true); + } + else { + if(log.isTraceEnabled()) + log.trace("service " + service + + " not found, cannot dispatch service view " + + service_view); + } + } + } + + /** + * Fetches the service states from everyone else in the cluster. Once all + * states have been received and inserted into service_state, compute a + * service view (a copy of MergeView) for each service and pass it up + * + * @param view + */ + private void handleMergeView(MergeView view) throws Exception { + long time_to_wait=service_response_timeout; + long start_time=System.currentTimeMillis(); + Map> copy=null; + + byte[] data=Util.objectToByteBuffer(new HashSet(services.keySet())); + + //loop and keep sending our service list until either + //we hit timeout or we get notification of merge completed + //http://jira.jboss.com/jira/browse/JGRP-665 + while(time_to_wait > 0 && !services_merged.get()) { + // we have to make this message OOB since we are running on a thread + // propelling a regular synchronous message call to install a new view + sendServiceMessage(false, ServiceInfo.LIST_SERVICES_RSP, null, data, true); + Util.sleep(500); + time_to_wait=service_response_timeout - (System.currentTimeMillis() - start_time); + } + + if(time_to_wait <= 0 && !services_merged.get()) { + log.warn("Services not merged at " + getLocalAddress() + + " received merge from " + + services_merged_collector); + } + + synchronized(service_responses) { + copy=new HashMap>(service_responses); + } + + if(log.isDebugEnabled()) + log.debug("At " + getLocalAddress() + " emitting views to MuxChannels " + copy); + + // merges service_responses with service_state and emits MergeViews for + // the services affected (MuxChannel) + mergeServiceState(view, copy); + } + + private void mergeServiceState(MergeView view, Map> copy) { + Set modified_services=new HashSet(); + synchronized(service_state) { + for(Iterator>> it=copy.entrySet().iterator();it.hasNext();) { + Map.Entry> entry=it.next(); + Address host=entry.getKey(); + Set service_list=entry.getValue(); + if(service_list == null) + continue; + + for(String service:service_list) { + List
my_services=service_state.get(service); + if(my_services == null) { + my_services=new CopyOnWriteArrayList
(); + service_state.put(service, my_services); + } + + boolean was_modified=my_services.add(host); + if(was_modified) { + modified_services.add(service); + } + } + } + } + + // now emit MergeViews for all services which were modified + for(String service:modified_services) { + MuxChannel ch=services.get(service); + if(ch != null) { + List
hosts=service_state.get(service); + Vector
membersCopy=new Vector
(view.getMembers()); + membersCopy.retainAll(hosts); + MergeView v=new MergeView(view.getVid(), membersCopy, view.getSubgroups()); + passToMuxChannel(ch, + new Event(Event.VIEW_CHANGE, v), + fifo_queue, + null, + service, + false); + } + } + } + + private void adjustServiceView(Address host) { + + Address local_address=getLocalAddress(); + synchronized(service_state) { + for(Iterator>> it=service_state.entrySet().iterator();it.hasNext();) { + Map.Entry> entry=it.next(); + String service=entry.getKey(); + List
hosts=entry.getValue(); + if(hosts == null) + continue; + + if(hosts.remove(host)) { + // make a copy so we don't modify hosts in + // generateServiceView() + View service_view=generateServiceView(new ArrayList
(hosts)); + MuxChannel ch=services.get(service); + if(ch != null && ch.isConnected()) { + Event view_evt=new Event(Event.VIEW_CHANGE, service_view); + passToMuxChannel(ch, view_evt, fifo_queue, null, service, false, true); + } + else { + if(log.isTraceEnabled()) + log.trace("service " + service + + " not found, cannot dispatch service view " + + service_view); + } + } + boolean isMyService=local_address != null && local_address.equals(host); + if(isMyService) + removeService(service); + } + } + } + + /** + * Create a copy of view which contains only members which are present in + * hosts. Call viewAccepted() on the MuxChannel which corresponds with + * service. If no members are removed or added from/to view, this is a + * no-op. + * + * @param hosts + * List
+ * @return the servicd view (a modified copy of the real view), or null if + * the view was not modified + */ + private View generateServiceView(List
hosts) { + if(view == null) { + Vector
tmp=new Vector
(); + tmp.add(getLocalAddress()); + view=new View(new ViewId(getLocalAddress()), tmp); + } + Vector
members=new Vector
(view.getMembers()); + members.retainAll(hosts); + return new View(view.getVid(), members); + } + + private Object passToMuxChannel(MuxChannel ch, + Event evt, + final FIFOMessageQueue queue, + final Address sender, + final String dest, + boolean block) { + return passToMuxChannel(ch, evt, queue, sender, dest, block, false); + } + + private Object passToMuxChannel(MuxChannel ch, + Event evt, + final FIFOMessageQueue queue, + final Address sender, + final String dest, + boolean block, + boolean bypass_thread_pool) { + if(thread_pool == null || bypass_thread_pool) { + return ch.up(evt); + } + + Task task=new Task(ch, evt, queue, sender, dest, block); + ExecuteTask execute_task=new ExecuteTask(fifo_queue); // takes Task from queue and executes it + + try { + fifo_queue.put(sender, dest, task); + thread_pool.execute(execute_task); + if(block) { + try { + return task.exchanger.exchange(null); + } + catch(InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } + catch(InterruptedException e) { + Thread.currentThread().interrupt(); + } + return null; + } + + private class MultiplexerChannelListener extends ChannelListenerAdapter { + + //handle reconnecting of services after being shunned and + //then reconnected back + @Override + public void channelReconnected(Address addr) { + if(log.isDebugEnabled()) + log.debug("Reconnecting services " + services.keySet()); + + for(MuxChannel mux_ch:services.values()) { + try { + if(log.isDebugEnabled()) + log.debug("Reconnecting service " + mux_ch.getId()); + + mux_ch.open(); + boolean reconnect=((Boolean)mux_ch.getOpt(Channel.AUTO_RECONNECT)).booleanValue(); + boolean getState=((Boolean)mux_ch.getOpt(Channel.AUTO_GETSTATE)).booleanValue(); + boolean fetchAndGetState=reconnect && getState; + if(fetchAndGetState) { + mux_ch.connect(mux_ch.getClusterName(), null, null, 10000); + mux_ch.fireChannelReconnected(mux_ch.getLocalAddress()); + } + else { + if(reconnect) { + mux_ch.connect(mux_ch.getClusterName()); + mux_ch.fireChannelReconnected(mux_ch.getLocalAddress()); + } + if(getState) { + mux_ch.getState(null, 5000); + } + } + } + catch(ChannelException e) { + if(log.isErrorEnabled()) + log.error("MuxChannel reconnect failed " + e); + } + } + } + + @Override + public void channelShunned() { + for(MuxChannel mux_ch:services.values()) { + mux_ch.fireChannelShunned(); + } + } + } + + private static class Task implements Runnable { + Exchanger exchanger; + MuxChannel channel; + Event evt; + FIFOMessageQueue queue; + Address sender; + String dest; + + Task(MuxChannel channel, + Event evt, + FIFOMessageQueue queue, + Address sender, + String dest, + boolean result_expected) { + this.channel=channel; + this.evt=evt; + this.queue=queue; + this.sender=sender; + this.dest=dest; + if(result_expected) + exchanger=new Exchanger(); + } + + public void run() { + Object retval; + try { + retval=channel.up(evt); + if(exchanger != null) + exchanger.exchange(retval); + } + catch(InterruptedException e) { + Thread.currentThread().interrupt(); // let the thread pool handle the interrupt - we're done anyway + } + finally { + queue.done(sender, dest); + } + } + } + + private static class ExecuteTask implements Runnable { + FIFOMessageQueue queue; + + public ExecuteTask(FIFOMessageQueue queue) { + this.queue=queue; + } + + public void run() { + try { + Runnable task=queue.take(); + task.run(); + } + catch(InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/mux/MuxChannel.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/mux/MuxChannel.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/mux/MuxChannel.java 17 Aug 2012 14:51:28 -0000 1.1 @@ -0,0 +1,425 @@ +package org.jgroups.mux; + +import org.jgroups.*; +import org.jgroups.stack.ProtocolStack; + +import java.io.Serializable; +import java.util.Map; + +/** + * Multiplexer channel is a lightweight version of a regular channel where + * multiple MuxChannel(s) share the same underlying regular channel. + * + *

+ * MuxChannel has to be created with a unique application id. The multiplexer + * keeps track of all registered applications and tags messages belonging to a + * specific application with that id for sent messages. When receiving a message + * from a remote peer, the multiplexer will dispatch a message to the + * appropriate MuxChannel depending on the id attached to the message. + * + *

+ * MuxChannel is created using + * {@link ChannelFactory#createMultiplexerChannel(String, String)}. + * + * @author Bela Ban, Vladimir Blagojevic + * @see ChannelFactory#createMultiplexerChannel(String, String) + * @see JChannelFactory#createMultiplexerChannel(String, String) + * @see Multiplexer + * @since 2.4 + * @version $Id: MuxChannel.java,v 1.1 2012/08/17 14:51:28 marcin Exp $ + */ +public class MuxChannel extends JChannel { + + /* + * Header identifier + */ + private static final String name="MUX"; + + /* + * MuxChannel service ID + */ + private final String id; + + /* + * The name of the JGroups stack, e.g. as defined in stacks.xml + */ + private final String stack_name; + + /* + * Header added to each message sent from this MuxChannel + */ + private final MuxHeader hdr; + + /* + * Underlying Multiplexer + */ + private final Multiplexer mux; + + MuxChannel(String id,String stack_name,Multiplexer mux) { + super(false); // don't create protocol stack, queues and threads + if(id == null || id.length() == 0) + throw new IllegalArgumentException("Cannot create MuxChannel with id " + id); + + if(stack_name == null || stack_name.length() == 0) + throw new IllegalArgumentException("Cannot create MuxChannel with stack_name " + stack_name); + + if(mux == null) + throw new IllegalArgumentException("Cannot create MuxChannel with Multiplexer " + mux); + + this.stack_name=stack_name; + this.id=id; + this.hdr=new MuxHeader(id); + this.mux=mux; + closed=!mux.isOpen(); + } + + public String getStackName() { + return stack_name; + } + + public String getId() { + return id; + } + + public Multiplexer getMultiplexer() { + return mux; + } + + public String getChannelName() { + return mux.getChannel().getClusterName(); + } + + public String getClusterName() { + return mux.getChannel().getClusterName(); + } + + public Address getLocalAddress() { + return mux.getLocalAddress(); + } + + public String getProperties() { + return mux.getChannel().getProperties(); + } + + /** This should never be used (just for testing) ! */ + public JChannel getChannel() { + return mux.getChannel(); + } + + /** + * Returns the service view, ie. the cluster view (see + * {@link #getView()}) minus the nodes on which this service is + * not running, e.g. if S1 runs on A and C, and the cluster view is {A,B,C}, + * then the service view is {A,C} + * + * @return The service view (list of nodes on which this service is running) + */ + public View getView() { + return closed || !connected? null : mux.getServiceView(id); + } + + /** + * Returns the JGroups view of a cluster, e.g. if we have nodes A, B and C, + * then the view will be {A,B,C} + * + * @return The JGroups view + */ + public View getClusterView() { + return mux.getChannel().getView(); + } + + public ProtocolStack getProtocolStack() { + return mux.getChannel().getProtocolStack(); + } + + public Map dumpStats() { + Map retval=mux.getChannel().getProtocolStack().dumpStats(); + if(retval != null) { + Map tmp=dumpChannelStats(); + if(tmp != null) + retval.put("channel", tmp); + } + return retval; + } + + protected void setClosed(boolean f) { + closed=f; + } + + protected void setConnected(boolean f) { + connected=f; + } + + public synchronized void connect(String channel_name) throws ChannelException, + ChannelClosedException { + /*make sure the channel is not closed*/ + checkClosed(); + + /*if we already are connected, then ignore this*/ + if(isConnected()) { + if(log.isTraceEnabled()) + log.trace("already connected to " + channel_name); + return; + } + //add service --> MuxChannel mapping to multiplexer in case we called disconnect on this channel + mux.addServiceIfNotPresent(getId(), this); + if(!mux.isConnected()) { + mux.connect(getStackName()); + } + try { + if(mux.flushSupported()) { + boolean successfulFlush=mux.startFlush(false); + if(!successfulFlush && log.isWarnEnabled()) { + log.warn("Flush failed at " + mux.getLocalAddress() + ":" + getId()); + } + } + try { + mux.sendServiceUpMessage(getId()); + setClosed(false); + setConnected(true); + notifyChannelConnected(this); + } + catch(Exception e) { + if(log.isErrorEnabled()) + log.error("failed sending SERVICE_UP message", e); + throw new ChannelException("MuxChannel.connect() failed", e); + } + } + finally { + if(mux.flushSupported()) + mux.stopFlush(); + } + } + + public synchronized void connect(String cluster_name, + Address target, + String state_id, + long timeout) throws ChannelException { + /*make sure the channel is not closed*/ + checkClosed(); + + /*if we already are connected, then ignore this*/ + if(isConnected()) { + if(log.isTraceEnabled()) + log.trace("already connected to " + cluster_name); + return; + } + //add service --> MuxChannel mapping to multiplexer in case we called disconnect on this channel + mux.addServiceIfNotPresent(getId(), this); + if(!mux.isConnected()) { + mux.connect(getStackName()); + } + try { + if(mux.flushSupported()) { + boolean successfulFlush=mux.startFlush(false); + if(!successfulFlush && log.isWarnEnabled()) { + log.warn("Flush failed at " + mux.getLocalAddress() + ":" + getId()); + } + } + try { + mux.sendServiceUpMessage(getId()); + setClosed(false); + setConnected(true); + notifyChannelConnected(this); + } + catch(Exception e) { + if(log.isErrorEnabled()) + log.error("failed sending SERVICE_UP message", e); + throw new ChannelException("MuxChannel.connect() failed", e); + } + View serviceView=mux.getServiceView(getId()); + boolean stateTransferOk=false; + boolean fetchState=serviceView != null && serviceView.size() > 1; + if(fetchState) { + stateTransferOk=getState(target, state_id, timeout, false); + if(!stateTransferOk) { + throw new StateTransferException("Could not retrieve state " + state_id + + " from " + + target); + } + } + } + finally { + if(mux.flushSupported()) + mux.stopFlush(); + } + } + + public synchronized void disconnect() { + if(!isConnected()) + return; + + setClosed(false); + setConnected(false); + notifyServiceDown(); + + // disconnects JChannel if all MuxChannels are + // in disconnected state + mux.disconnect(); + notifyChannelDisconnected(this); + } + + public synchronized void close() { + if(closed) + return; + + if(isConnected()) { + setConnected(false); + notifyServiceDown(); + } + setClosed(true); + + closeMessageQueue(true); + notifyChannelClosed(this); + } + + protected void notifyServiceDown() { + try { + if(mux.flushSupported()) { + boolean successfulFlush=mux.startFlush(false); + if(!successfulFlush && log.isWarnEnabled()) { + log.warn("Flush failed at " + mux.getLocalAddress() + ":" + getId()); + } + } + try { + mux.sendServiceDownMessage(getId()); + } + catch(Exception e) { + if(log.isErrorEnabled()) + log.error("failed sending SERVICE_DOWN message", e); + } + } + catch(Throwable t) { + log.error("closing channel failed", t); + } + finally { + if(mux.flushSupported()) + mux.stopFlush(); + } + } + + public synchronized void open() throws ChannelException { + setClosed(false); + setConnected(false); // needs to be connected next + if(!mux.isOpen()) { + mux.open(); + } + } + + public synchronized void shutdown() { + if(closed) + return; + + setClosed(true); + setConnected(false); + + try { + if(mux.flushSupported()) { + boolean successfulFlush=mux.startFlush(false); + if(!successfulFlush && log.isWarnEnabled()) { + log.warn("Flush failed at " + mux.getLocalAddress() + ":" + getId()); + } + } + try { + mux.sendServiceDownMessage(getId()); + } + catch(Exception e) { + if(log.isErrorEnabled()) + log.error("failed sending SERVICE_DOWN message", e); + } + } + catch(Throwable t) { + log.error("shutdown channel failed", t); + } + finally { + if(mux.flushSupported()) + mux.stopFlush(); + } + closeMessageQueue(true); + notifyChannelClosed(this); + } + + public void send(Message msg) throws ChannelNotConnectedException,ChannelClosedException { + msg.putHeader(name, hdr); + mux.getChannel().send(msg); + if(stats) { + sent_msgs++; + sent_bytes+=msg.getLength(); + } + } + + public void send(Address dst, Address src, Serializable obj) throws ChannelNotConnectedException, + ChannelClosedException { + send(new Message(dst, src, obj)); + } + + public void down(Event evt) { + if(evt.getType() == Event.MSG) { + Message msg=(Message)evt.getArg(); + msg.putHeader(name, hdr); + } + mux.getChannel().down(evt); + } + + public Object downcall(Event evt) { + if(evt.getType() == Event.MSG) { + Message msg=(Message)evt.getArg(); + msg.putHeader(name, hdr); + } + return mux.getChannel().downcall(evt); + } + + public boolean getState(Address target, String state_id, long timeout, boolean useFlushIfPresent) throws ChannelNotConnectedException, + ChannelClosedException { + String my_id=id; + + if(state_id != null) + my_id+="::" + state_id; + + // we're usig service views, so we need to find the first host in the cluster on which our service runs + // http://jira.jboss.com/jira/browse/JGRP-247 + // + // unless service runs on a specified target node + // http://jira.jboss.com/jira/browse/JGRP-401 + Address service_view_coordinator=mux.getStateProvider(target, id); + Address tmp=getLocalAddress(); + + if(service_view_coordinator != null) + target=service_view_coordinator; + + if(tmp != null && tmp.equals(target)) // this will avoid the "cannot get state from myself" error + target=null; + + if(!mux.stateTransferListenersPresent()) + return mux.getChannel().getState(target, my_id, timeout, useFlushIfPresent); + else { + View serviceView=mux.getServiceView(getId()); + boolean fetchState=serviceView != null && serviceView.size() > 1; + if(fetchState) { + return mux.getState(target, my_id, timeout); + } + else { + return false; + } + } + } + + void fireChannelShunned(){ + notifyChannelShunned(); + } + + void fireChannelReconnected(Address address){ + notifyChannelReconnected(address); + } + + public void returnState(byte[] state) { + mux.getChannel().returnState(state, id); + } + + public void returnState(byte[] state, String state_id) { + String my_id=id; + if(state_id != null) + my_id+="::" + state_id; + mux.getChannel().returnState(state, my_id); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/mux/MuxHeader.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/mux/MuxHeader.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/mux/MuxHeader.java 17 Aug 2012 14:51:28 -0000 1.1 @@ -0,0 +1,84 @@ +package org.jgroups.mux; + +import org.jgroups.Global; +import org.jgroups.Header; +import org.jgroups.util.Streamable; +import org.jgroups.util.Util; + +import java.io.*; + +/** + * Header used for multiplexing and de-multiplexing between service components on top of a Multiplexer (Channel) + * @author Bela Ban + * @version $Id: MuxHeader.java,v 1.1 2012/08/17 14:51:28 marcin Exp $ + */ +public class MuxHeader extends Header implements Streamable { + String id=null; + + /** Used for service state communication between Multiplexers */ + ServiceInfo info; + private static final long serialVersionUID=9197570523315316128L; + + public MuxHeader() { + } + + public MuxHeader(String id) { + this.id=id; + } + + public MuxHeader(ServiceInfo info) { + this.info=info; + } + + public String getId() { + return id; + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeUTF(id); + out.writeObject(info); + } + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + id=in.readUTF(); + info=(ServiceInfo)in.readObject(); + } + + + public int size() { + int retval=Global.BYTE_SIZE; // presence byte in Util.writeString + if(id != null) + retval+=id.length() +2; // for UTF + retval+=Global.BYTE_SIZE; // presence for info + if(info != null) + retval+=info.size(); + return retval; + } + + public void writeTo(DataOutputStream out) throws IOException { + Util.writeString(id, out); + if(info != null) { + out.writeBoolean(true); + info.writeTo(out); + } + else { + out.writeBoolean(false); + } + } + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + id=Util.readString(in); + if(in.readBoolean()) { + info=new ServiceInfo(); + info.readFrom(in); + } + } + + public String toString() { + if(id != null) + return id; + if(info != null) + return info.toString(); + return ""; + } +} Index: 3rdParty_sources/jgroups/org/jgroups/mux/ServiceInfo.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/mux/ServiceInfo.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/mux/ServiceInfo.java 17 Aug 2012 14:51:28 -0000 1.1 @@ -0,0 +1,130 @@ +package org.jgroups.mux; + +import org.jgroups.Address; +import org.jgroups.Global; +import org.jgroups.util.Streamable; +import org.jgroups.util.Util; + +import java.io.*; + +/** + * Class used for service state communication between Multiplexers + * @author Bela Ban + * @version $Id: ServiceInfo.java,v 1.1 2012/08/17 14:51:28 marcin Exp $ + */ +public class ServiceInfo implements Externalizable, Streamable { + public static final byte SERVICE_UP = 3; + public static final byte SERVICE_DOWN = 4; + public static final byte LIST_SERVICES_RSP = 5; // list of services available on a given node (available in 'state') + public static final byte ACK = 6; + public static final byte SERVICES_MERGED = 7; + + byte type=0; + String service=null; + Address host=null; + byte[] state=null; + + + public ServiceInfo() { + } + + public ServiceInfo(byte type, String service, Address host, byte[] state) { + this.type=type; + this.service=service; + this.host=host; + this.state=state; + } + + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeByte(type); + out.writeUTF(service); + out.writeObject(host); + if(state != null) { + out.writeInt(state.length); + out.write(state); + } + else { + out.writeInt(0); + } + } + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + type=in.readByte(); + service=in.readUTF(); + host=(Address)in.readObject(); + int len=in.readInt(); + if(len > 0) { + state=new byte[len]; + in.readFully(state, 0, len); + } + } + + + public long size() { + long retval=Global.BYTE_SIZE; // type + retval+=Global.BYTE_SIZE; // presence byte for service + if(service != null) + retval+=service.length() +2; + retval+=Util.size(host); + retval+=Global.INT_SIZE; // length of state + if(state != null) + retval+=state.length; + return retval; + } + + public void writeTo(DataOutputStream out) throws IOException { + out.writeByte(type); + Util.writeString(service, out); + Util.writeAddress(host, out); + if(state != null) { + out.writeInt(state.length); + out.write(state, 0, state.length); + } + else { + out.writeInt(0); + } + } + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + type=in.readByte(); + service=Util.readString(in); + host=Util.readAddress(in); + int len=in.readInt(); + if(len > 0) { + state=new byte[len]; + in.readFully(state, 0, len); + } + } + + + + public String toString() { + switch(type) { + case SERVICE_UP: return "SERVICE_UP(" + service + "," + host + ")"; + case SERVICE_DOWN: return "SERVICE_DOWN(" + service + "," + host + ")"; + case ACK: return "ACK"; + case SERVICES_MERGED: return "SERVICES_MERGED("+ host + ")"; + case LIST_SERVICES_RSP: + String services=null; + try { + services=Util.objectFromByteBuffer(state).toString(); + } + catch(Exception e) { + } + return "LIST_SERVICES_RSP(" + services + ")"; + default: return "n/a"; + } + } + + public static String typeToString(int t) { + switch(t) { + case SERVICE_UP: return "SERVICE_UP"; + case SERVICE_DOWN: return "SERVICE_DOWN"; + case ACK: return "ACK"; + case SERVICES_MERGED: return "SERVICES_MERGED"; + case LIST_SERVICES_RSP: return "LIST_SERVICES_RSP"; + default: return "n/a"; + } + } +} Index: 3rdParty_sources/jgroups/org/jgroups/persistence/CannotConnectException.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/persistence/CannotConnectException.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/persistence/CannotConnectException.java 17 Aug 2012 14:51:21 -0000 1.1 @@ -0,0 +1,51 @@ +package org.jgroups.persistence; + +/** + * @author Mandar Shinde + * This exception inherits the Exception class and is used in + * cases where the Persistence Storage cannot be connected to + * for any purpose. + */ + + +public class CannotConnectException extends Exception +{ + + private static final long serialVersionUID = 7472528586067210747L; + + /** + * @param t + * @param reason implementor-specified runtime reason + */ + public CannotConnectException(Exception t, String reason) + { + this.t = t; + this.reason = reason; + } + + /** + * @param t + * @param reason implementor-specified runtime reason + */ + public CannotConnectException(Throwable t, String reason) + { + this.t = t; + this.reason = reason; + } + + /** + * @return String; + */ + public String toString() + { + String tmp = "Exception " + t.toString() + " was thrown due to " + reason; + return tmp; + } + + /** + * members are made available so that the top level user can dump + * appropriate members on to his stack trace + */ + public Throwable t = null; + public String reason = null; +} Index: 3rdParty_sources/jgroups/org/jgroups/persistence/CannotCreateSchemaException.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/persistence/CannotCreateSchemaException.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/persistence/CannotCreateSchemaException.java 17 Aug 2012 14:51:21 -0000 1.1 @@ -0,0 +1,41 @@ +package org.jgroups.persistence; + +/** + * @author Mandar Shinde + * This exception inherits the Exception class and is used in + * cases where the Persistence Storage cannot create schema to + * use the API as required. At this point, top level use needs to + * decide whether to continue or abort. + */ + +public class CannotCreateSchemaException extends Exception +{ + + private static final long serialVersionUID = 291582260022140141L; + + /** + * @param t + * @param reason implementor-specified runtime reason + */ + public CannotCreateSchemaException(Throwable t, String reason) + { + this.t = t; + this.reason = reason; + } + + /** + * @return String + */ + public String toString() + { + String tmp = "Exception " + t.toString() + " was thrown due to " + reason; + return tmp; + } + + /** + * members are made available so that the top level user can dump + * appropriate members on to his stack trace + */ + private Throwable t = null; + private String reason = null; +} Index: 3rdParty_sources/jgroups/org/jgroups/persistence/CannotPersistException.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/persistence/CannotPersistException.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/persistence/CannotPersistException.java 17 Aug 2012 14:51:21 -0000 1.1 @@ -0,0 +1,40 @@ +package org.jgroups.persistence; + +/** + * @author Mandar Shinde + * This exception inherits the Exception class and is used in + * cases where the Persistence Storage cannot persist a pair + * from its storage mechanism (leading to fatal errors) + */ + +public class CannotPersistException extends Exception +{ + + private static final long serialVersionUID = -5157400778265186170L; + + /** + * @param t + * @param reason implementor-specified runtime reason + */ + public CannotPersistException(Throwable t, String reason) + { + this.t = t; + this.reason = reason; + } + + /** + * @return String; + */ + public String toString() + { + String tmp = "Exception " + t.toString() + " was thrown due to " + reason; + return tmp; + } + + /** + * members are made available so that the top level user can dump + * appropriate members on to his stack trace + */ + private Throwable t = null; + private String reason = null; +} Index: 3rdParty_sources/jgroups/org/jgroups/persistence/CannotRemoveException.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/persistence/CannotRemoveException.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/persistence/CannotRemoveException.java 17 Aug 2012 14:51:21 -0000 1.1 @@ -0,0 +1,40 @@ +package org.jgroups.persistence; + +/** + * @author Mandar Shinde + * This exception inherits the Exception class and is used in + * cases where the Persistence Storage cannot remove a pair + * from its storage mechanism (leading to permannt errors) + */ + +public class CannotRemoveException extends Exception +{ + + private static final long serialVersionUID = -5777024088921236116L; + + /** + * @param t + * @param reason implementor-specified runtime reason + */ + public CannotRemoveException(Throwable t, String reason) + { + this.t = t; + this.reason = reason; + } + + /** + * @return String; + */ + public String toString() + { + String tmp = "Exception " + t.toString() + " was thrown due to " + reason; + return tmp; + } + + /** + * members are made available so that the top level user can dump + * appropriate members on to his stack trace + */ + public Throwable t = null; + public String reason = null; +} Index: 3rdParty_sources/jgroups/org/jgroups/persistence/CannotRetrieveException.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/persistence/CannotRetrieveException.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/persistence/CannotRetrieveException.java 17 Aug 2012 14:51:21 -0000 1.1 @@ -0,0 +1,40 @@ +package org.jgroups.persistence; + +/** + * @author Mandar Shinde + * This exception inherits the Exception class and is used in + * cases where the Persistence Storage cannot retrieve a pair + * from its storage mechanism (leading to failure of mechanism) + */ + +public class CannotRetrieveException extends Exception +{ + + private static final long serialVersionUID = -2523227229540681597L; + + /** + * @param t + * @param reason implementor-specified runtime reason + */ + public CannotRetrieveException(Throwable t, String reason) + { + this.t = t; + this.reason = reason; + } + + /** + * @return String; + */ + public String toString() + { + String tmp = "Exception " + t.toString() + " was thrown due to " + reason; + return tmp; + } + + /** + * members are made available so that the top level user can dump + * appropriate members on to his stack trace + */ + private Throwable t = null; + private String reason = null; +} Index: 3rdParty_sources/jgroups/org/jgroups/persistence/DBPersistenceManager.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/persistence/DBPersistenceManager.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/persistence/DBPersistenceManager.java 17 Aug 2012 14:51:21 -0000 1.1 @@ -0,0 +1,687 @@ +package org.jgroups.persistence; + +/** + * @author Mandar Shinde + * This class implements the DB storage pattern for the Persistence + * Manager interface. The implementation is open and can be used (and + * tested) over more than one databases. It uses a string (VARCHAR) + * as the key and either BLOB or VARBINARY db-datatype for the + * serialized objects. THe user has the option to choose his/her own + * schema over the ones provided. + */ + + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.*; +import java.sql.*; +import java.util.*; + + +/** + * Class will be utilized + */ +public class DBPersistenceManager implements PersistenceManager { + + protected final Log log=LogFactory.getLog(this.getClass()); + + /** + * Default construct + * @param filename absolute filepath + * @exception Exception; + */ + public DBPersistenceManager(String filename) throws Exception { + String home_dir = null; + + // PropertyPermission not granted if running in an untrusted environment with JNLP. + try { + home_dir = System.getProperty("user.home"); + } + catch (SecurityException ex1) { + } + + // 1. Try ${user.home}/persist.properties + try { + home_dir=home_dir + '/' + filename; + init(new FileInputStream(home_dir)); + return; + } + catch(Exception ex) { + ; + } + + // 2. Try to find persist.properties from somewhere on the CLASSPATH + try { + InputStream in=DBPersistenceManager.class.getResourceAsStream('/' + filename); + if(in != null) { + init(in); + return; + } + } + catch(Exception x) { + if(log.isErrorEnabled()) log.error("failed reading database properties from " + filename + ", exception=" + x); + } + + // 3. Finally maybe the user specified -Dpersist.properties=/home/user/mypersist.properties + try { + home_dir=System.getProperty("persist.properties"); + init(new FileInputStream(home_dir)); + return; + } + catch(Exception ex) { + ; + } + + // 4. If none of the above helped us to find persist.properties, give up and throw an exception + throw new Exception("DBPersistenceManager.DBPersistenceManager(): " + + "failed reading database properties from " + filename); + } + + + /** + * Duplicate constructor allowing inputstream + * @param input + * @exception Exception + */ + public DBPersistenceManager(InputStream input) throws Exception { + init(input); + } + + + /** + * used to intitiailize complete DB access. THis method will use + * existing database to create schema (if it doesnt exist) and + * get PersistenceManager in usable condition + * @param in + * @exception Exception; + */ + protected void init(InputStream in) throws Exception { + list=new Vector(); + readProps(in); + loadDriver(); + + //check conn + Connection conn=this.getConnection(); + this.closeConnection(conn); + createDBTables(); + retrieveAll(); // work around to make sure, no duplicates are created. + log.error(" Done constructing DB Persist Manager"); + } + + + // TODO list for this implementation + // add constructor for xml file + // add constructor for default + + + /** + * Saves NV pair as serializable object; + * creates if new, stores new state if already exists. + * @param key + * @param val + * @exception CannotPersistException; + */ + public void save(Serializable key, Serializable val) throws CannotPersistException { + // checking if this is update or new entry + if(!entryExists(key)) { + log.error(" entry doesnt exist for " + key.toString()); + try { + addNewEntry(key, val); + list.add(key.toString()); + return; + } + catch(Throwable t1) { + t1.printStackTrace(); + //trace here + throw new CannotPersistException(t1, " error adding a completely new entry in to DB "); + } + }// checking entries + + // THis is for regular updates to the key,val pair + Connection conn=null; + PreparedStatement prepStat=null; + try { + conn=this.getConnection(); + String keyStr=null; + keyStr=key.toString(); + byte[] keyBytes=getBytes(key); + byte[] valBytes=getBytes(val); + log.error(" value is " + val); + //use simple execute, do not create prepared statement + prepStat=conn.prepareStatement(updateStat); + prepStat.setString(3, keyStr); + prepStat.setBytes(1, keyBytes); + prepStat.setBytes(2, valBytes); + prepStat.executeQuery(); + } + catch(Throwable t) { + //trace here + t.printStackTrace(); + // throw exception here + throw new CannotPersistException(t, "error updating an existing entry in to the database "); + } + // cleanup + finally { + try { + if(prepStat != null) prepStat.close(); + this.closeConnection(conn); + } + catch(Throwable t) { + // trace + conn=null; + prepStat=null; + } + } + } + + + /** + * Removes existing entry. + * @param key + * @exception CannotRemoveException; + */ + public Serializable remove(Serializable key) throws CannotRemoveException { + Connection conn=null; + Statement stat=null; + PreparedStatement prepStat=null; + ResultSet set=null; + Serializable val=null; + + try { + conn=this.getConnection(); + stat=conn.createStatement(); + String exQuery=" select * from replhashmap where key like '" + key.toString() + '\''; + set=stat.executeQuery(exQuery); + set.next(); + val=getSerializable(set.getBinaryStream(3)); + } + catch(Throwable t3) { + //trace + t3.printStackTrace(); + throw new CannotRemoveException(t3, " Error retrieving value for given key"); + } + finally { + try { + if(prepStat != null) prepStat.close(); + this.closeConnection(conn); + } + catch(Throwable t) { + // trace + conn=null; + prepStat=null; + } + } + + + try { + conn=this.getConnection(); + prepStat=conn.prepareStatement(removeStat); + prepStat.setString(1, key.toString()); + prepStat.executeQuery(); + list.remove(key.toString()); + } + catch(Throwable t) { + //trace here.. + t.printStackTrace(); + // throw Exception + throw new CannotRemoveException(t, "Could not remove existing entry due to error in jdbc transaction"); + } + + // cleanup + finally { + try { + set.close(); + stat.close(); + if(prepStat != null) prepStat.close(); + this.closeConnection(conn); + } + catch(Throwable t) { + // trace + conn=null; + stat=null; + }//end of try..catch + }// end of finally.. + return val; + }// end of remove + + + /** + * Saves all row entries for the map to DB. + * @param map + * @exception CannotPersistException; + */ + public synchronized void saveAll(Map map) throws CannotPersistException { + Iterator iter=null; + try { + Set keySet=map.keySet(); + iter=keySet.iterator(); + } + catch(Throwable t) { + t.printStackTrace(); + //trace here + throw new CannotPersistException(t, "Error with the map entered to saveAll"); + } + + //Individually saving all + while(iter.hasNext()) { + try { + Serializable key=(Serializable) iter.next(); + Serializable val=(Serializable) map.get(key); + + // dont this in same thread, optimization can be added + this.save(key, val); + } + catch(Throwable t2) { + t2.printStackTrace(); + //trace here + continue; + } + }// end of while.. + }// end of saveall + + + /** + * Used to retrieve the persisted map back to its last known state + * @return Map; + * @exception CannotRetrieveException; + */ + public synchronized Map retrieveAll() throws CannotRetrieveException { + Connection conn=null; + Statement stat=null; + ResultSet set=null; + Map map=null; + try { + conn=this.getConnection(); + stat=conn.createStatement(); + set=stat.executeQuery(" select * from replhashmap"); + map=retrieveAll(set); + } + catch(Throwable t) { + //trace here + throw new CannotRetrieveException(t, "Error happened while querying the database for bulk retrieve, try starting DB manually"); + } + + + //finally + try { + stat.close(); + this.closeConnection(conn); + } + catch(Throwable t1) { + // trace it + // ignore + } + + return map; + }// end of retrieveall + + + /** + * Helper method to get get back the map + * @return Map; + * @exception Exception; + */ + private Map retrieveAll(ResultSet result) throws Exception { + HashMap map=new HashMap(); + while(result.next()) { + InputStream inputStrKey=result.getBinaryStream(2); + InputStream inputStrVal=result.getBinaryStream(3); + Serializable key=getSerializable(inputStrKey); + Serializable val=getSerializable(inputStrVal); + map.put(key, val); + list.add(key.toString()); + }// end of while.. + return map; + } + + + /** + * Clears the key-cache as well as all entries + * @exception CannotRemoveException; + */ + public void clear() throws CannotRemoveException { + Connection conn=null; + Statement stat=null; + try { + conn=this.getConnection(); + stat=conn.createStatement(); + stat.executeQuery("delete from replhashmap"); + } + catch(Throwable t) { + //trace here + throw new CannotRemoveException(t, " delete all query failed with existing database"); + } + + //finally + try { + stat.close(); + this.closeConnection(conn); + } + catch(Throwable t) { + conn=null; + stat=null; + } + } + + + /** + * Shutting down the database cleanly + */ + public void shutDown() { + // non-trivial problem, more research required + // no-op for now.. + } + + + + /** + * The private interfaces are used specifically to this manager + */ + + /** + * Used to enter a completely new row in to the current table + * @param Serializable; key + * @param Serializable; value + * @exception CannotPersistException; + */ + private void addNewEntry(Serializable key, Serializable val) throws CannotPersistException, CannotConnectException { + Connection conn=getConnection(); + try { + PreparedStatement prepStat=conn.prepareStatement(insertStat); + prepStat.setString(1, key.toString()); + byte[] keyBytes=getBytes(key); + byte[] valBytes=getBytes(val); + //InputStream keyStream = getBinaryInputStream(key); + //InputStream valStream = getBinaryInputStream(val); + prepStat.setBytes(2, keyBytes); + prepStat.setBytes(3, valBytes); + //prepStat.setBinaryStream(keyStream); + //prepStat.setBinaryStream(valStream); + prepStat.executeQuery(); + conn.commit(); + log.error(" executing insert " + insertStat); + } + catch(Throwable t) { + //conn.rollback(); + t.printStackTrace(); + //trace here + throw new CannotPersistException(t, "error adding new entry using creating Db connection and schema"); + } + }// end of addentry.. + + + /** + * Gets a binaryinputstream from a serialized object + * @param Serializable; + * @return BinaryInputStream; + * @exception Exception; + */ + private java.io.InputStream getBinaryInputStream(Serializable ser) throws Exception { + ByteArrayOutputStream stream=new ByteArrayOutputStream(); + ObjectOutputStream keyoos=new ObjectOutputStream(stream); + keyoos.writeObject(ser); + ByteArrayInputStream pipe=new ByteArrayInputStream(stream.toByteArray()); + return pipe; + }// end of stream conversion + + + /** + * Gets a serializable back from a InputStream + * @param InputStream; + * @return Serializable; + * @exception Exception; + */ + private Serializable getSerializable(java.io.InputStream stream) throws Exception { + ObjectInputStream ooStr=new ObjectInputStream(stream); + Serializable tmp=(Serializable) ooStr.readObject(); + return tmp; + } + + + /** + * Used to enter a completely new row in to the current table + * @param Serializable; key + * @param Serializable; value + * @exception CannotPersistException; + */ + private void addNewEntryGen(Serializable key, Serializable val) throws CannotPersistException, CannotConnectException { + Connection conn=getConnection(); + try { + PreparedStatement prepStat=conn.prepareStatement(insertStat); + prepStat.setString(1, key.toString()); + prepStat.setBytes(2, getBytes(key)); + prepStat.setBytes(3, getBytes(val)); + prepStat.executeUpdate(); + } + catch(Throwable t) { + //trace here + throw new CannotPersistException(t, "error adding new entry using creating Db connection and schema"); + } + }// end of entering new row gen + + /** + * Used to enter a completely new row in to the current table + * @param Serializable; key + * @param Serializable; value + * @exception CannotPersistException; + */ + private void addNewEntryOra(Serializable key, Serializable val) throws CannotPersistException, CannotConnectException { + Connection conn=getConnection(); + try { + PreparedStatement prepStat=conn.prepareStatement(insertStat); + prepStat.setString(1, key.toString()); + InputStream keyBin=getBinaryInputStream(key); + InputStream keyVal=getBinaryInputStream(val); + byte[] keyBytes=getBytes(key); + byte[] valBytes=getBytes(val); + prepStat.setBytes(2, keyBytes); + prepStat.setBytes(3, valBytes); + prepStat.executeBatch(); + } + catch(Throwable t) { + //trace here + throw new CannotPersistException(t, "error adding new entry using creating Db connection and schema"); + } + }// end of entering new row ora + + + /** + * Cache checking + * @param java.io.Serializable + * @return boolean; + */ + private boolean entryExists(Serializable key) { + return list.contains(key.toString()); + } + + + /** + * Conversion helper + * @param Serializable; + * @return byte[]; + */ + private byte[] getBytes(Serializable ser) throws Exception { + ByteArrayOutputStream stream=new ByteArrayOutputStream(); + ObjectOutputStream keyoos=new ObjectOutputStream(stream); + keyoos.writeObject(ser); + byte[] keyBytes=stream.toByteArray(); + return keyBytes; + }// end of getBytes + + + + + /** + * ALL IMPL below is for INIT purposes + */ + + /** + * This method will be invoked by defauly by each persistence + * manager to read from a default location or one provided by + * the caller. + * @return void; + * @exception Exception; + */ + private void readProps(String filePath) throws Exception { + FileInputStream _stream=new FileInputStream(filePath); + props=new Properties(); + props.load(_stream); + + // using properties to set most used variables + driverName=props.getProperty("jdbc.Driver"); + connStr=props.getProperty("jdbc.Conn").trim(); + userName=props.getProperty("jdbc.User").trim(); + userPass=props.getProperty("jdbc.Pass").trim(); + createTable=props.getProperty("jdbc.table").trim(); + } + + + /** + * Duplicate reader using stream instead of dile + * @param InputStream; + * @exception Exception; + */ + private void readProps(InputStream input) throws Exception { + props=new Properties(); + props.load(input); + + // using properties to set most used variables + driverName=props.getProperty("jdbc.Driver"); + connStr=props.getProperty("jdbc.Conn"); + userName=props.getProperty("jdbc.User"); + userPass=props.getProperty("jdbc.Pass"); + createTable=props.getProperty("jdbc.table"); + } + + + /** + * Loads the driver using the driver class name. Drivers can be simply + * loaded by loading the class or by registering specifically using the + * JDBC DriverManager + * @return void; + * @exception Exception; + */ + private void loadDriver() throws Exception { + // driver classes when loaded load the driver into VM + Class.forName(driverName); + } + + + /** + * Once the driver is loaded, the DB is ready to be connected. This + * method provides a handle to connect to the DB. + * @return Connection; + * @exception CannotConnectException; + */ + private Connection getConnection() throws CannotConnectException { + try { + connStr=connStr.trim(); + Connection conn=DriverManager.getConnection(connStr, userName, userPass); + if(log.isInfoEnabled()) log.info("userName=" + userName + + ", userPass=" + userPass + ", connStr=" + connStr); + return conn; + } + catch(Throwable t) { + t.printStackTrace(); + //trace here + throw new CannotConnectException(t, "Error in creating connection using provided properties "); + } + }// end of get conn.. + + + /** + * Method is used for closing created connection. + * Pooling is not implemented currently, but will be made available + * as soon as this manager uses large number of transactions + * @param Connection + */ + private void closeConnection(Connection conn) { + try { + if(conn != null) { + conn.close(); + conn=null; + } + } + catch(Throwable t) { + //trace here + conn=null; + } + }// end of closeConn + + + /** + * Used to create table provided the DB instance + * @exception CannotCreateSchemaException; + * @exception CannotConnectException; + */ + private void createDBTables() throws CannotCreateSchemaException, CannotConnectException { + Connection conn=this.getConnection(); + Statement stat=null; + try { + stat=conn.createStatement(); + } + catch(Exception e) { + //trace here.. + e.printStackTrace(); + throw new CannotConnectException(e, "there was an error in creating statements for persisting data using created connection"); + } + try { + ResultSet set=stat.executeQuery("select * from replhashmap"); + } + catch(Throwable t) { + t.printStackTrace(); + //use connection to create new statement + addSchemaToDB(conn); + }// end of out throwable.. + }// end of method.. + + + /** + * used to create required table within the DB + * @param Connection; + * @exception CannotCreateSchema; + */ + private void addSchemaToDB(Connection conn) throws CannotCreateSchemaException { + Statement stat=null; + Statement stat2=null; + try { + + stat=conn.createStatement(); + log.error(" executing query for oracle " + createTable); + stat.executeQuery(createTable); + } + catch(Throwable t) { + t.printStackTrace(); + // trace here + throw new CannotCreateSchemaException(t, "error was using schema with blobs"); + }// end of catch + + // clean up is required after init + finally { + try { + if(stat != null) stat.close(); + this.closeConnection(conn); + } + catch(Throwable t3) { + } + }// end of finally.. + }// end of gen schema.. + + private Properties props=null; + private String driverName=null; + private String userName=null; + private String userPass=null; + private String connStr=null; + private String createTable=null; + private final boolean oracleDB=false; + private Vector list=null; + + + private static final String tabName="replhashmap"; + private static final String insertStat="insert into replhashmap(key, keyBin, valBin) values (?, ?, ?)"; + private static final String updateStat="update replhashmap set keyBin = ?, valBin = ? where key like ?"; + private static final String removeStat=" delete from replhashmap where key like ?"; + private static final String createTableGen=" create table replhashmap(key varchar, keyBin varbinary, valBin varbinary)"; + private static final String createTableOra=" create table replhashmap ( key varchar2(100), keyBin blob, valBin blob)"; +} Index: 3rdParty_sources/jgroups/org/jgroups/persistence/FilePersistenceManager.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/persistence/FilePersistenceManager.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/persistence/FilePersistenceManager.java 17 Aug 2012 14:51:21 -0000 1.1 @@ -0,0 +1,167 @@ +package org.jgroups.persistence; + +/** + * @author Mandar Shinde + * The class implements the PersistenceManager interface and provides users + * a file based implementation when required. + * The state of this class is current NOOP. Implementation will be in place + * once a better structure for file based properties will be designed. + */ + + +import java.io.*; +import java.util.*; + +public class FilePersistenceManager implements PersistenceManager +{ + private final File file; + + /** + * Default constructor + */ + public FilePersistenceManager(String propertiesFilename) + throws Exception + { + Properties properties = new Properties(); + properties.load(new FileInputStream(propertiesFilename)); + String path = properties.getProperty(PersistenceFactory.persistProp); + file = new File(path); + file.createNewFile(); + } + + /** + * Save new NV pair as serializable objects or if already exist; store + * new state + */ + public void save(Serializable key, Serializable val) throws CannotPersistException + { + try + { + Map map = retrieveAll(); + map.put(key, val); + saveAll(map); + } + catch (CannotRetrieveException e) + { + throw new CannotPersistException(e, "Unable to pre-load existing store."); + } + } + + /** + * Remove existing NV from being persisted + */ + public Serializable remove(Serializable key) throws CannotRemoveException + { + Object o; + try + { + Map map = retrieveAll(); + o = map.remove(key); + saveAll(map); + } + catch (CannotRetrieveException e) + { + throw new CannotRemoveException(e, "Unable to pre-load existing store."); + } + catch (CannotPersistException e) + { + throw new CannotRemoveException(e, "Unable to pre-load existing store."); + } + return (Serializable) o; + } + + + /** + * Use to store a complete map into persistent state + * @exception CannotPersistException; + */ + public void saveAll(Map map) throws CannotPersistException + { + try + { + OutputStream fos = new FileOutputStream(file); + Properties prop = new Properties(); + // NB: For some reason Properties.putAll(map) doesn't seem to work - dimc@users.sourceforge.net + for (Iterator iterator = map.entrySet().iterator(); iterator.hasNext();) + { + Map.Entry entry = (Map.Entry) iterator.next(); + prop.setProperty(entry.getKey().toString(), entry.getValue().toString()); + } + prop.store(fos, null); + fos.flush(); + fos.close(); + } + catch (IOException e) + { + throw new CannotPersistException(e, "Cannot save to: " + file.getAbsolutePath()); + } + } + + + /** + * Gives back the Map in last known state + * @return Map; + * @exception CannotRetrieveException; + */ + public Map retrieveAll() throws CannotRetrieveException + { + try + { + Properties prop = new Properties(); + FileInputStream fis = new FileInputStream(file); + prop.load(fis); + fis.close(); + return filterLoadedValues(prop); + } + catch (IOException e) + { + throw new CannotRetrieveException(e, "Unable to load from file: " + file.getAbsolutePath()); + } + } + + /** + * Turns the values into Floats to enable + * {@link org.jgroups.demos.DistributedHashtableDemo} to work. + * Subclasses should override this method to convert the incoming map + * of string/string key/value pairs into the types they want. + * @param in + * @return Map + */ + protected Map filterLoadedValues(Map in) + { + Map out = new HashMap(); + for (Iterator iterator = in.entrySet().iterator(); iterator.hasNext();) + { + Map.Entry entry = (Map.Entry) iterator.next(); + out.put(entry.getKey().toString(), Float.valueOf(entry.getValue().toString())); + } + return out; + } + + + /** + * Clears the complete NV state from the DB + * @exception CannotRemoveException; + x*/ + public void clear() throws CannotRemoveException + { + try + { + saveAll(Collections.EMPTY_MAP); + } + catch (CannotPersistException e) + { + throw new CannotRemoveException(e, "Unable to clear map."); + } + } + + + /** + * Used to handle shutdown call the PersistenceManager implementation. + * Persistent engines can leave this implementation empty. + */ + public void shutDown() + { + return; + } +}// end of class Index: 3rdParty_sources/jgroups/org/jgroups/persistence/PersistenceFactory.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/persistence/PersistenceFactory.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/persistence/PersistenceFactory.java 17 Aug 2012 14:51:21 -0000 1.1 @@ -0,0 +1,201 @@ +package org.jgroups.persistence; + +/** + * @author Mandar Shinde + * This class is the factory to get access to any DB based or file based + * implementation. None of the implementations should expose directly + * to user for migration purposes + */ + + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.util.Util; + +import java.io.FileInputStream; +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.util.Properties; + + +public class PersistenceFactory +{ + + protected final static Log log=LogFactory.getLog(PersistenceFactory.class); + + /** + * Default private constructor// does nothing + */ + private PersistenceFactory() + { + } + + + /** + * Singular public method to get access to any of the Persistence + * Manager implementations. It is important to known at this point + * that properties determine the implementation of the Persistence + * Manager, there is no direct interface which gives access to + * either DB implemented ot FILE implemented storage API. + * @return PersistenceFactory; + */ + public static PersistenceFactory getInstance() { + log.debug(" getting factory instance "); + if (_factory == null) + _factory = new PersistenceFactory(); + return _factory; + } + + /** + * Register a custom persistence manager as opposed to the + * {@link FilePersistenceManager} or {@link DBPersistenceManager}. + */ + public synchronized void registerManager(PersistenceManager manager) + { + _manager = manager; + } + + /** + * Reads the default properties and creates a persistencemanager + * The default properties are picked up from the $USER_HOME or + * from the classpath. Default properties are represented by + * "persist.properties" + * @return PersistenceManager + * @exception Exception; + */ + public synchronized PersistenceManager createManager() throws Exception { + // will return null if not initialized + // uses default properties + if (_manager == null) + { + if (checkDB()) + _manager = createManagerDB(propPath); + else + _manager = createManagerFile(propPath); + } + return _manager; + } + + + /** + * Duplicated signature to create PersistenceManager to allow user to + * provide property path. + * @param filePath complete pathname to get the properties + * @return PersistenceManager; + * @exception Exception; + */ + public synchronized PersistenceManager createManager (String filePath) throws Exception + { + if (_manager == null) + { + if (checkDB(filePath)) + _manager = createManagerDB(filePath); + else + _manager = createManagerFile(filePath); + } + return _manager; + } + + + + /** + * Internal creator of DB persistence manager, the outside user accesses only + * the PersistenceManager interface API + */ + private PersistenceManager createManagerDB(String filePath) throws Exception + { + + if(log.isInfoEnabled()) log.info("Calling db persist from factory: " + filePath); + if (_manager == null) + _manager = new DBPersistenceManager(filePath); + return _manager; + }// end of DB persistence + + /** + * creates instance of file based persistency manager + * @return PersistenceManager + */ + private PersistenceManager createManagerFile(String filePath) + { + + if(log.isInfoEnabled()) log.info("Creating file manager: " + filePath); + Properties props; + + try + { + if (_manager == null) + { + props=readProps(filePath); + String classname=props.getProperty(filePersistMgr); + if(classname != null) + { + Class cl=Util.loadClass(classname, this.getClass()); + Constructor ctor=cl.getConstructor(new Class[]{String.class}); + _manager=(PersistenceManager)ctor.newInstance(new Object[]{filePath}); + } + else + { + _manager = new FilePersistenceManager(filePath); + } + } + return _manager; + } + catch (Throwable t) + { + t.printStackTrace(); + return null; + } + }// end of file persistence + + + /** + * checks the default properties for DB/File flag + * @return boolean; + * @exception Exception; + */ + private boolean checkDB() throws Exception + { + Properties props=readProps(propPath); + String persist = props.getProperty(persistProp); + if ("DB".equals(persist)) + return true; + return false; + } + + + + + /** + * checks the provided properties for DB/File flag + * @return boolean; + */ + private boolean checkDB(String filePath) throws Exception + { + Properties props=readProps(filePath); + String persist = props.getProperty(persistProp); + if ("DB".equals(persist)) + return true; + return false; + } + + + Properties readProps(String fileName) throws IOException + { + Properties props; + FileInputStream _stream = new FileInputStream(fileName); + props=new Properties(); + props.load(_stream); + return props; + } + + private static volatile PersistenceManager _manager = null; + private static volatile PersistenceFactory _factory = null; + + + /* Please set this according to configuration */ + final static String propPath = "persist.properties"; + final static String persistProp = "persist"; + + /** The class that implements a file-based PersistenceManager */ + final static String filePersistMgr="filePersistMgr"; +} Index: 3rdParty_sources/jgroups/org/jgroups/persistence/PersistenceManager.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/persistence/PersistenceManager.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/persistence/PersistenceManager.java 17 Aug 2012 14:51:21 -0000 1.1 @@ -0,0 +1,65 @@ +package org.jgroups.persistence; + +/** + * @author Mandar Shinde + * This interface defines the interface that needs to be implemented to + * persist any Map(Serializable) object. Primary usage would be users who + * need to store the state of a given NV for fault tolerance. + */ + + +import java.io.Serializable; +import java.util.Map; + + +public interface PersistenceManager +{ + + /** + * Save new NV pair as serializable objects or if already exist; store + * new state + * @param key + * @param val + * @exception CannotPersistException; + */ + void save(Serializable key, Serializable val) throws CannotPersistException; + + /** + * Remove existing NV from being persisted + * @param key value + * @return Serializable; gives back the value + * @exception CannotRemoveException; + */ + Serializable remove(Serializable key) throws CannotRemoveException; + + + /** + * Use to store a complete map into persistent state + * @param map + * @exception CannotPersistException; + */ + void saveAll(Map map) throws CannotPersistException; + + + /** + * Gives back the Map in last known state + * @return Map; + * @exception CannotRetrieveException; + */ + Map retrieveAll() throws CannotRetrieveException; + + + /** + * Clears the complete NV state from the DB + * @exception CannotRemoveException; + */ + void clear() throws CannotRemoveException; + + + /** + * Used to handle shutdown call the PersistenceManager implementation. + * Persistent engines can leave this implementation empty. + */ + void shutDown(); + +} Index: 3rdParty_sources/jgroups/org/jgroups/persistence/package.html =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/persistence/package.html,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/persistence/package.html 17 Aug 2012 14:51:21 -0000 1.1 @@ -0,0 +1,5 @@ + + + Provides features for storing information to a database or file. + + \ No newline at end of file Index: 3rdParty_sources/jgroups/org/jgroups/protocols/AUTH.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/AUTH.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/AUTH.java 17 Aug 2012 14:51:06 -0000 1.1 @@ -0,0 +1,211 @@ +package org.jgroups.protocols; + + +import org.jgroups.Address; +import org.jgroups.Event; +import org.jgroups.Message; +import org.jgroups.auth.AuthToken; +import org.jgroups.protocols.pbcast.GMS; +import org.jgroups.protocols.pbcast.JoinRsp; +import org.jgroups.stack.Protocol; +import java.util.Properties; + + +/** + * The AUTH protocol adds a layer of authentication to JGroups + * @author Chris Mills + */ +public class AUTH extends Protocol{ + + static final String NAME = "AUTH"; + + /** + * used on the coordinator to authentication joining member requests against + */ + private AuthToken serverSideToken = null; + + public AUTH(){ + } + + public boolean setProperties(Properties props) { + + String authClassString = props.getProperty("auth_class"); + + if(authClassString != null){ + props.remove("auth_class"); + + try{ + Object obj = Class.forName(authClassString).newInstance(); + serverSideToken = (AuthToken) obj; + serverSideToken.setValue(props); + }catch(Exception e){ + if(log.isFatalEnabled()){ + log.fatal("Failed to create server side token (" + authClassString + ")"); + log.fatal(e); + } + return false; + } + } + + if(!props.isEmpty()) { + //this should never happen as everything is read in to the AuthToken instance + if(log.isErrorEnabled()){ + log.error("AUTH.setProperties(): the following properties are not recognized: " + props); + } + return false; + } + return true; + } + + public final String getName() { + return AUTH.NAME; + } + /** + * Used to create a failed JOIN_RSP message to pass back down the stack + * @param joiner The Address of the requesting member + * @param message The failure message to send back to the joiner + * @return An Event containing a GmsHeader with a JoinRsp object + */ + private Event createFailureEvent(Address joiner, String message){ + Message msg = new Message(joiner, null, null); + + if(log.isDebugEnabled()){ + log.debug("Creating JoinRsp with failure message - " + message); + } + JoinRsp joinRes = new JoinRsp(message); + //need to specify the error message on the JoinRsp object once it's been changed + + GMS.GmsHeader gmsHeader = new GMS.GmsHeader(GMS.GmsHeader.JOIN_RSP, joinRes); + msg.putHeader(GMS.name, gmsHeader); + + if(log.isDebugEnabled()){ + log.debug("GMSHeader created for failure JOIN_RSP"); + } + + return new Event(Event.MSG, msg); + } + + /** + * An event was received from the layer below. Usually the current layer will want to examine + * the event type and - depending on its type - perform some computation + * (e.g. removing headers from a MSG event type, or updating the internal membership list + * when receiving a VIEW_CHANGE event). + * Finally the event is either a) discarded, or b) an event is sent down + * the stack using down_prot.down() or c) the event (or another event) is sent up + * the stack using up_prot.up(). + */ + public Object up(Event evt) { + GMS.GmsHeader hdr = isJoinMessage(evt); + if((hdr != null) && (hdr.getType() == GMS.GmsHeader.JOIN_REQ)){ + if(log.isDebugEnabled()){ + log.debug("AUTH got up event"); + } + //we found a join message - now try and get the AUTH Header + Message msg = (Message)evt.getArg(); + + if((msg.getHeader(AUTH.NAME) != null) && (msg.getHeader(AUTH.NAME) instanceof AuthHeader)){ + AuthHeader authHeader = (AuthHeader)msg.getHeader(AUTH.NAME); + + if(authHeader != null){ + //Now we have the AUTH Header we need to validate it + if(this.serverSideToken.authenticate(authHeader.getToken(), msg)){ + //valid token + if(log.isDebugEnabled()){ + log.debug("AUTH passing up event"); + } + up_prot.up(evt); + }else{ + //invalid token + if(log.isWarnEnabled()){ + log.warn("AUTH failed to validate AuthHeader token"); + } + sendRejectionMessage(msg.getSrc(), createFailureEvent(msg.getSrc(), "Authentication failed")); + } + }else{ + //Invalid AUTH Header - need to send failure message + if(log.isWarnEnabled()){ + log.warn("AUTH failed to get valid AuthHeader from Message"); + } + sendRejectionMessage(msg.getSrc(), createFailureEvent(msg.getSrc(), "Failed to find valid AuthHeader in Message")); + } + }else{ + if(log.isDebugEnabled()){ + log.debug("No AUTH Header Found"); + } + //should be a failure + sendRejectionMessage(msg.getSrc(), createFailureEvent(msg.getSrc(), "Failed to find an AuthHeader in Message")); + } + }else{ + //if debug + if(log.isDebugEnabled()){ + log.debug("Message not a JOIN_REQ - ignoring it"); + } + return up_prot.up(evt); + } + return null; + } + + + private void sendRejectionMessage(Address dest, Event join_rsp) { + if(dest == null) { + log.error("destination is null, cannot send JOIN rejection message to null destination"); + return; + } + down_prot.down(new Event(Event.ENABLE_UNICASTS_TO, dest)); + down_prot.down(join_rsp); + down_prot.down(new Event(Event.DISABLE_UNICASTS_TO, dest)); + } + + /** + * An event is to be sent down the stack. The layer may want to examine its type and perform + * some action on it, depending on the event's type. If the event is a message MSG, then + * the layer may need to add a header to it (or do nothing at all) before sending it down + * the stack using down_prot.down(). In case of a GET_ADDRESS event (which tries to + * retrieve the stack's address from one of the bottom layers), the layer may need to send + * a new response event back up the stack using up_prot.up(). + */ + public Object down(Event evt) { + GMS.GmsHeader hdr = isJoinMessage(evt); + if((hdr != null) && (hdr.getType() == GMS.GmsHeader.JOIN_REQ)){ + if(log.isDebugEnabled()){ + log.debug("AUTH got down event"); + } + //we found a join request message - now add an AUTH Header + Message msg = (Message)evt.getArg(); + AuthHeader authHeader = new AuthHeader(); + authHeader.setToken(this.serverSideToken); + msg.putHeader(AUTH.NAME, authHeader); + + if(log.isDebugEnabled()){ + log.debug("AUTH passing down event"); + } + } + + if((hdr != null) && (hdr.getType() == GMS.GmsHeader.JOIN_RSP)){ + if(log.isDebugEnabled()){ + log.debug(hdr.toString()); + } + } + + return down_prot.down(evt); + } + + /** + * Used to check if the message type is a Gms message + * @param evt The event object passed in to AUTH + * @return A GmsHeader object or null if the event contains a message of a different type + */ + private static GMS.GmsHeader isJoinMessage(Event evt){ + Message msg; + switch(evt.getType()){ + case Event.MSG: + msg = (Message)evt.getArg(); + Object obj = msg.getHeader("GMS"); + if(obj == null || !(obj instanceof GMS.GmsHeader)){ + return null; + } + return (GMS.GmsHeader)obj; + } + return null; + } +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/AUTOCONF.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/AUTOCONF.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/AUTOCONF.java 17 Aug 2012 14:51:05 -0000 1.1 @@ -0,0 +1,222 @@ +// $Id: AUTOCONF.java,v 1.1 2012/08/17 14:51:05 marcin Exp $ + +package org.jgroups.protocols; + +import org.jgroups.Event; +import org.jgroups.stack.Protocol; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + + +/** + * Senses the network configuration when it is initialized (in init()) and sends a CONFIG event up + * and down the stack. The CONFIG event contains a hashmap, with strings as keys (e.g. "frag_size") + * and Objects as values. Certain protocols can set some of their properties when receiving the CONFIG + * event. + *

+ * This protocol should be placed above the transport protocol (e.g. UDP). It is not needed for TCP. + *

+ * Example: senses the network send and receive buffers, plus the max size of a message to be sent and + * generates a CONFIG event containing "frag_size", "send_buf_size" and "receive_buf_size" keys. + * + * @author Bela Ban + */ +public class AUTOCONF extends Protocol { + final Map config=new HashMap(); + static int num_iterations=10; // to find optimal frag_size + + /** Number of bytes to subtract from computed fragmentation size, due to (a) headers and + * (b) serialization overhead */ + static int frag_overhead=1000; + + + public String getName() { + return "AUTOCONF"; + } + + + public void init() throws Exception { + senseNetworkConfiguration(); + if(log.isDebugEnabled()) log.debug("configuration is\n" + config); + } + + public void start() throws Exception { + if(config != null && config.size() > 0) { + Event config_evt=new Event(Event.CONFIG, config); + down_prot.down(config_evt); + up_prot.up(config_evt); + } + } + + + /** + * Setup the Protocol instance acording to the configuration string + */ + public boolean setProperties(Properties props) { + String str; + + super.setProperties(props); + str=props.getProperty("num_iterations"); + if(str != null) { + num_iterations=Integer.parseInt(str); + props.remove("num_iterations"); + } + + str=props.getProperty("frag_overhead"); + if(str != null) { + frag_overhead=Integer.parseInt(str); + props.remove("frag_overhead"); + } + + if(props.size() > 0) { + log.error("AUTOCONF.setProperties(): the following properties are not recognized: " + props); + return false; + } + return true; + } + + + + + /* -------------------------------------- Private metods ------------------------------------------- */ + void senseNetworkConfiguration() { + int max_frag_size=senseMaxFragSize(); + if(max_frag_size <= 0) { + if(log.isErrorEnabled()) log.error("max_frag_size is invalid: " + max_frag_size); + } + else + config.put("frag_size", new Integer(max_frag_size)); + senseMaxSendBufferSize(config); + senseMaxReceiveBufferSize(config); + } + + public static int senseMaxFragSizeStatic() { + return new AUTOCONF().senseMaxFragSize(); + } + + /** + * Tries to find out the max number of bytes in a DatagramPacket we can send by sending increasingly + * larger packets, until there is an exception (e.g., java.io.IOException: message too long). + */ + public int senseMaxFragSize() { + int max_send=32000; + int upper; + int lower=0; + int highest_failed=-1; + DatagramSocket sock; + byte[] buf; + DatagramPacket packet; + InetAddress local_addr; + + + try { + sock=new DatagramSocket(); + local_addr=InetAddress.getLocalHost(); + } + catch(Exception ex) { + if(log.isWarnEnabled()) log.warn("failed creating DatagramSocket: " + ex); + return 0; + } + + try { + upper=max_send; + for(int i=0; i < num_iterations && lower < upper; i++) { // iterations to approximate frag_size + try { + buf=new byte[upper]; + // System.out.println("** upper=" + upper + " (lower=" + lower + ")"); + packet=new DatagramPacket(buf, buf.length, local_addr, 9); + sock.send(packet); + lower=Math.max(lower, upper); + upper=upper * 2; + if(highest_failed > -1) + upper=Math.min(highest_failed, upper); + } + catch(IOException io_ex) { + if(highest_failed > -1) + highest_failed=Math.min(highest_failed, upper); // never exceed max_upper + else + highest_failed=upper; + upper=(upper + lower) / 2; + } + catch(Throwable ex) { + if(log.isWarnEnabled()) log.warn("exception=" + ex); + break; + } + } + + /** Reduce the frag_size a bit to prevent packets that are too large (see bug #854887) */ + lower-=frag_overhead; + if(log.isDebugEnabled()) log.debug("frag_size=" + lower); + return lower; + } + finally { + if(sock != null) + sock.close(); + } + } + + + void senseMaxSendBufferSize(Map map) { + DatagramSocket sock; + int max_size=4096, retval=max_size; + + if(map != null && map.containsKey("frag_size)")) + max_size=((Integer)map.get("frag_size")).intValue(); + + try { + sock=new DatagramSocket(); + while(max_size < 1000000) { + sock.setSendBufferSize(max_size); + if((retval=sock.getSendBufferSize()) < max_size) + return; + max_size*=2; + } + } + catch(Throwable ex) { + if(log.isErrorEnabled()) log.error("failed getting the max send buffer size: " + ex + + ". Defaulting to " + retval); + } + finally { + map.put("send_buf_size", new Integer(retval)); + } + } + + + + void senseMaxReceiveBufferSize(Map map) { + DatagramSocket sock; + int max_size=4096, retval=max_size; + + try { + sock=new DatagramSocket(); + while(max_size < 1000000) { + sock.setReceiveBufferSize(max_size); + if((retval=sock.getReceiveBufferSize()) < max_size) + return; + max_size*=2; + } + } + catch(Throwable ex) { + if(log.isErrorEnabled()) log.error("failed getting the max send buffer size: " + ex + + ". Defaulting to " + retval); + } + finally { + map.put("recv_buf_size", new Integer(retval)); + } + } + + + /* ----------------------------------- End of Private metods --------------------------------------- */ + + public static void main(String[] args) { + int frag_size=new AUTOCONF().senseMaxFragSize(); + System.out.println("frag_size: " + frag_size); + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/AuthHeader.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/AuthHeader.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/AuthHeader.java 17 Aug 2012 14:51:05 -0000 1.1 @@ -0,0 +1,54 @@ +package org.jgroups.protocols; + +import org.jgroups.Header; +import org.jgroups.auth.AuthToken; +import org.jgroups.util.Streamable; +import org.jgroups.util.Util; + +import java.io.*; +/** + * AuthHeader is a holder object for the token that is passed from the joiner to the coordinator + * @author Chris Mills + */ +public class AuthHeader extends Header implements Streamable{ + private AuthToken token=null; + + public AuthHeader(){ + } + /** + * Sets the token value to that of the passed in token object + * @param token the new authentication token + */ + public void setToken(AuthToken token){ + this.token = token; + } + + /** + * Used to get the token from the AuthHeader + * @return the token found inside the AuthHeader + */ + public AuthToken getToken(){ + return this.token; + } + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + this.token = (AuthToken)in.readObject(); + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeObject(this.token); + } + + public void writeTo(DataOutputStream out) throws IOException { + Util.writeAuthToken(this.token, out); + } + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + this.token = Util.readAuthToken(in); + } + public int size(){ + //need to fix this + return Util.sizeOf(this); + } +} + Index: 3rdParty_sources/jgroups/org/jgroups/protocols/BARRIER.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/BARRIER.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/BARRIER.java 17 Aug 2012 14:51:06 -0000 1.1 @@ -0,0 +1,233 @@ +package org.jgroups.protocols; + +import org.jgroups.Event; +import org.jgroups.stack.Protocol; +import org.jgroups.util.TimeScheduler; + +import java.util.Properties; +import java.util.Set; +import java.util.HashSet; +import java.util.Iterator; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +/** + * All messages up the stack have to go through a barrier (read lock, RL). By default, the barrier is open. + * When a CLOSE_BARRIER event is received, we close the barrier by acquiring a write lock (WL). This succeeds when all + * previous messages have completed (by releasing their RLs). Thus, when we have acquired the WL, we know that there + * are no pending messages processed.
+ * When an OPEN_BARRIER event is received, we simply open the barrier again and let all messages pass in the up + * direction. This is done by releasing the WL. + * @author Bela Ban + * @version $Id: BARRIER.java,v 1.1 2012/08/17 14:51:06 marcin Exp $ + */ + +public class BARRIER extends Protocol { + long max_close_time=60000; // how long can the barrier stay closed (in ms) ? 0 means forever + final Lock lock=new ReentrantLock(); + final AtomicBoolean barrier_closed=new AtomicBoolean(false); + + /** signals to waiting threads that the barrier is open again */ + Condition barrier_opened=lock.newCondition(); + Condition no_msgs_pending=lock.newCondition(); + ConcurrentMap in_flight_threads=new ConcurrentHashMap(); + Future barrier_opener_future=null; + TimeScheduler timer; + private static final Object NULL=new Object(); + + + public String getName() { + return "BARRIER"; + } + + + public boolean setProperties(Properties props) { + String str; + super.setProperties(props); + str=props.getProperty("max_close_time"); + if(str != null) { + max_close_time=Long.parseLong(str); + props.remove("max_close_time"); + } + + if(!props.isEmpty()) { + log.error("these properties are not recognized: " + props); + return false; + } + return true; + } + + public boolean isClosed() { + return barrier_closed.get(); + } + + public boolean isOpenerScheduled() { + return barrier_opener_future != null && !barrier_opener_future.isDone() && !barrier_opener_future.isCancelled(); + } + + public long getMaxCloseTime() { + return max_close_time; + } + + public int getNumberOfInFlightThreads() { + return in_flight_threads.size(); + } + + public void init() throws Exception { + super.init(); + timer=getTransport().getTimer(); + } + + public void stop() { + super.stop(); + openBarrier(); + } + + + public void destroy() { + super.destroy(); + openBarrier(); + } + + public Object down(Event evt) { + switch(evt.getType()) { + case Event.CLOSE_BARRIER: + closeBarrier(); + return null; + case Event.OPEN_BARRIER: + openBarrier(); + return null; + } + return down_prot.down(evt); + } + + public Object up(Event evt) { + switch(evt.getType()) { + case Event.MSG: + Thread current_thread=Thread.currentThread(); + in_flight_threads.put(current_thread, NULL); + if(barrier_closed.get()) { + lock.lock(); + try { + // Feb 28 2008 (Gray Watson): remove myself because barrier is closed + in_flight_threads.remove(current_thread); + while(barrier_closed.get()) { + try { + barrier_opened.await(); + } + catch(InterruptedException e) { + } + } + } + finally { + // Feb 28 2008 (Gray Watson): barrier is now open, put myself back in_flight + in_flight_threads.put(current_thread, NULL); + lock.unlock(); + } + } + + try { + return up_prot.up(evt); + } + finally { + lock.lock(); + try { + if(in_flight_threads.remove(current_thread) == NULL && + in_flight_threads.isEmpty() && + barrier_closed.get()) { + no_msgs_pending.signalAll(); + } + } + finally { + lock.unlock(); + } + } + case Event.CLOSE_BARRIER: + closeBarrier(); + return null; + case Event.OPEN_BARRIER: + openBarrier(); + return null; + } + return up_prot.up(evt); + } + + + /** Close the barrier. Temporarily remove all threads which are waiting or blocked, re-insert them after the call */ + private void closeBarrier() { + if(!barrier_closed.compareAndSet(false, true)) + return; // barrier was already closed + + Set threads=new HashSet(); + + lock.lock(); + try { + // wait until all pending (= in-progress, runnable threads) msgs have returned + in_flight_threads.remove(Thread.currentThread()); + while(!in_flight_threads.isEmpty()) { + for(Iterator it=in_flight_threads.keySet().iterator(); it.hasNext();) { + Thread thread=it.next(); + Thread.State state=thread.getState(); + if(state != Thread.State.RUNNABLE && state != Thread.State.NEW) { + threads.add(thread); + it.remove(); + } + } + if(!in_flight_threads.isEmpty()) { + try { + no_msgs_pending.await(1000, TimeUnit.MILLISECONDS); + } + catch(InterruptedException e) { + } + } + } + } + finally { + for(Thread thread: threads) + in_flight_threads.put(thread, NULL); + lock.unlock(); + } + + if(log.isTraceEnabled()) + log.trace("barrier was closed"); + + if(max_close_time > 0) + scheduleBarrierOpener(); + } + + private void openBarrier() { + lock.lock(); + try { + if(!barrier_closed.compareAndSet(true, false)) + return; // barrier was already open + barrier_opened.signalAll(); + } + finally { + lock.unlock(); + } + if(log.isTraceEnabled()) + log.trace("barrier was opened"); + cancelBarrierOpener(); // cancels if running + } + + private void scheduleBarrierOpener() { + if(barrier_opener_future == null || barrier_opener_future.isDone()) { + barrier_opener_future=timer.schedule(new Runnable() {public void run() {openBarrier();}}, + max_close_time, TimeUnit.MILLISECONDS + ); + } + } + + private void cancelBarrierOpener() { + if(barrier_opener_future != null) { + barrier_opener_future.cancel(true); + barrier_opener_future=null; + } + } +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/BSH.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/BSH.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/BSH.java 17 Aug 2012 14:51:06 -0000 1.1 @@ -0,0 +1,224 @@ +// $Id: BSH.java,v 1.1 2012/08/17 14:51:06 marcin Exp $ + +package org.jgroups.protocols; + + +import bsh.EvalError; +import bsh.Interpreter; +import org.jgroups.Address; +import org.jgroups.Event; +import org.jgroups.Header; +import org.jgroups.Message; +import org.jgroups.stack.Protocol; +import org.jgroups.util.Util; + +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.io.Serializable; + + + + +/** + * Beanshell (www.beanshell.org) interpreter class. + * The eval() method receives Java code, executes it and returns the + * result of the evaluation (or an exception). + * User: Bela + * Date: Mar 8, 2003 + * Time: 1:57:07 PM + * @author Bela Ban + */ +public class BSH extends Protocol { + static final String name="BSH"; + Interpreter interpreter=null; + + public BSH() { + } + + public String getName() { + return name; + } + + + + public void init() throws Exception { + } + + public void start() throws Exception { + } + + public void stop() { + if(interpreter != null) + destroyInterpreter(); + } + + public void destroy() { + } + + + public Object up(Event evt) { + Header h; + Message msg; + int type; + + if(evt.getType() == Event.MSG) { + msg=(Message)evt.getArg(); + h=msg.getHeader(name); + if(h instanceof BshHeader) { + type=((BshHeader)h).type; + switch(type) { + case BshHeader.REQ: + handleRequest(msg.getSrc(), msg.getBuffer()); + return null; + case BshHeader.RSP: + msg.putHeader(name, h); + up_prot.up(evt); + return null; + case BshHeader.DESTROY_INTERPRETER: + destroyInterpreter(); + return null; + default: + if(log.isErrorEnabled()) log.error("header type was not REQ as expected (was " + type + ')'); + return null; + } + } + } + return up_prot.up(evt); + } + + + void handleRequest(Address sender, byte[] buf) { + Object retval; + String code; + + + if(buf == null) { + if(log.isErrorEnabled()) log.error("buffer was null"); + return; + } + + code=new String(buf); + + // create interpreter just-in-time + if(interpreter == null) { + interpreter=new Interpreter(); + + if(log.isInfoEnabled()) log.info("beanshell interpreter was created"); + try { + interpreter.set("bsh_prot", this); + + if(log.isInfoEnabled()) log.info("set \"bsh_prot\" to " + this); + } + catch(EvalError err) { + if(log.isErrorEnabled()) log.error("failed setting \"bsh_prot\": " + err); + } + + } + + try { + retval=interpreter.eval(code); + + if(log.isInfoEnabled()) log.info("eval: \"" + code + + "\", retval=" + retval); + } + catch(EvalError ex) { + if(log.isErrorEnabled()) log.error("error is " + Util.getStackTrace(ex)); + retval=ex; + } + + if(sender != null) { + Message rsp=new Message(sender, null, null); + + // serialize the object if serializable, otherwise just send string + // representation + if(retval != null) { + if(retval instanceof Serializable) + rsp.setObject((Serializable)retval); + else + rsp.setObject(retval.toString()); + } + + + if(log.isInfoEnabled()) log.info("sending back response " + + retval + " to " + rsp.getDest()); + rsp.putHeader(name, new BshHeader(BshHeader.RSP)); + down_prot.down(new Event(Event.MSG, rsp)); + } + } + + +/* --------------------------- Callbacks ---------------------------- */ +// public Object eval(String code) throws Exception { +// Object retval=null; +// try { +// retval=interpreter.eval(code); +// +// if(log.isInfoEnabled()) log.info("BSH.eval()", "eval: \"" + code + +// "\", retval=" + retval); +// if(retval != null && !(retval instanceof Serializable)) { +// if(log.isErrorEnabled()) log.error("BSH.eval", "return value " + retval + +// " is not serializable, cannot be sent back " + +// "(returning null)"); +// return null; +// } +// return retval; +// } +// catch(EvalError ex) { +// if(log.isErrorEnabled()) log.error("BSH.eval()", "error is " + Util.getStackTrace(ex)); +// return ex; +// } +// } +// + public void destroyInterpreter() { + interpreter=null; // allow it to be garbage collected + + if(log.isInfoEnabled()) log.info("beanshell interpreter was destroyed"); + } + /* ------------------------ End of Callbacks ------------------------ */ + + + public static class BshHeader extends Header { + public static final int REQ=1; + public static final int RSP=2; + public static final int DESTROY_INTERPRETER=3; + int type=REQ; + + + public BshHeader() { + } + + public BshHeader(int type) { + this.type=type; + } + + public int size() { + return 10; + } + + public String toString() { + StringBuilder sb=new StringBuilder(); + if(type == REQ) + sb.append("REQ"); + else + if(type == RSP) + sb.append("RSP"); + else + if(type == DESTROY_INTERPRETER) + sb.append("DESTROY_INTERPRETER"); + else + sb.append(""); + return sb.toString(); + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeInt(type); + } + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + type=in.readInt(); + } + + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/BasicTCP.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/BasicTCP.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/BasicTCP.java 17 Aug 2012 14:51:06 -0000 1.1 @@ -0,0 +1,273 @@ +package org.jgroups.protocols; + +import org.jgroups.Address; +import org.jgroups.Event; +import org.jgroups.Message; +import org.jgroups.stack.Protocol; +import org.jgroups.util.BoundedList; +import org.jgroups.util.Util; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Collection; +import java.util.Properties; +import java.util.Set; + +/** + * Shared base class for tcpip protocols + * @author Scott Marlow + */ +public abstract class BasicTCP extends TP { + + /** Should we drop unicast messages to suspected members or not */ + boolean skip_suspected_members=true; + + /** When we cannot send a message to P (on an exception), then we send a SUSPECT message up the stack */ + boolean suspect_on_send_failure=false; + + + /** List the maintains the currently suspected members. This is used so we don't send too many SUSPECT + * events up the stack (one per message !) + */ + final BoundedList

suspected_mbrs=new BoundedList
(20); + protected InetAddress external_addr=null; // the IP address which is broadcast to other group members + protected int start_port=7800; // find first available port starting at this port + protected int end_port=0; // maximum port to bind to + protected long reaper_interval=0; // time in msecs between connection reaps + protected long conn_expire_time=0; // max time a conn can be idle before being reaped + /** Use separate send queues for each connection */ + boolean use_send_queues=true; + int send_queue_size=10000; // max number of messages in a send queue + int recv_buf_size=150000; + int send_buf_size=150000; + int sock_conn_timeout=2000; // max time in millis for a socket creation in ConnectionTable + int peer_addr_read_timeout=1000; // max time to block on reading of peer address + boolean tcp_nodelay=false; + int linger=-1; // SO_LINGER (number of ms, -1 disables it) + + + + public int getStartPort() {return start_port;} + public void setStartPort(int start_port) {this.start_port=start_port;} + public int getEndPort() {return end_port;} + public void setEndPort(int end_port) {this.end_port=end_port;} + public long getReaperInterval() {return reaper_interval;} + public void setReaperInterval(long reaper_interval) {this.reaper_interval=reaper_interval;} + public long getConnExpireTime() {return conn_expire_time;} + public void setConnExpireTime(long conn_expire_time) {this.conn_expire_time=conn_expire_time;} + + public boolean setProperties(Properties props) { + String str; + + super.setProperties(props); + + str=props.getProperty("start_port"); + if(str != null) { + start_port=Integer.parseInt(str); + props.remove("start_port"); + } + + //https://jira.jboss.org/jira/browse/JGRP-865 + str=props.getProperty("bind_port"); + if(str != null) { + start_port=Integer.parseInt(str); + props.remove("bind_port"); + } + + str=props.getProperty("end_port"); + if(str != null) { + end_port=Integer.parseInt(str); + props.remove("end_port"); + } + + str=props.getProperty("external_addr"); + if(str != null) { + try { + external_addr=InetAddress.getByName(str); + } + catch(UnknownHostException unknown) { + if(log.isFatalEnabled()) log.fatal("(external_addr): host " + str + " not known"); + return false; + } + props.remove("external_addr"); + } + + str=props.getProperty("reaper_interval"); + if(str != null) { + reaper_interval=Long.parseLong(str); + props.remove("reaper_interval"); + } + + str=props.getProperty("conn_expire_time"); + if(str != null) { + conn_expire_time=Long.parseLong(str); + props.remove("conn_expire_time"); + } + + str=props.getProperty("sock_conn_timeout"); + if(str != null) { + sock_conn_timeout=Integer.parseInt(str); + props.remove("sock_conn_timeout"); + } + + str=props.getProperty("peer_addr_read_timeout"); + if(str != null) { + peer_addr_read_timeout=Integer.parseInt(str); + props.remove("peer_addr_read_timeout"); + } + + str=props.getProperty("recv_buf_size"); + if(str != null) { + recv_buf_size=Integer.parseInt(str); + props.remove("recv_buf_size"); + } + + str=props.getProperty("send_buf_size"); + if(str != null) { + send_buf_size=Integer.parseInt(str); + props.remove("send_buf_size"); + } + + str=props.getProperty("skip_suspected_members"); + if(str != null) { + skip_suspected_members=Boolean.valueOf(str).booleanValue(); + props.remove("skip_suspected_members"); + } + + str=props.getProperty("suspect_on_send_failure"); + if(str != null) { + suspect_on_send_failure=Boolean.valueOf(str).booleanValue(); + props.remove("suspect_on_send_failure"); + } + + str=props.getProperty("use_send_queues"); + if(str != null) { + use_send_queues=Boolean.valueOf(str).booleanValue(); + props.remove("use_send_queues"); + } + + str=props.getProperty("send_queue_size"); + if(str != null) { + send_queue_size=Integer.parseInt(str); + props.remove("send_queue_size"); + } + + str=props.getProperty("tcp_nodelay"); + if(str != null) { + tcp_nodelay=Boolean.parseBoolean(str); + props.remove("tcp_nodelay"); + } + + str=props.getProperty("linger"); + if(str != null) { + linger=Integer.parseInt(str); + if(linger > 0) { + linger=Math.min(1, linger / 1000); // convert from ms to secs + } + props.remove("linger"); + } + + + Util.checkBufferSize(getName() + ".recv_buf_size", recv_buf_size); + Util.checkBufferSize(getName() + ".send_buf_size", send_buf_size); + + return true; + } + + public void init() throws Exception { + super.init(); + if(start_port <= 0) { + Protocol dynamic_discovery_prot=stack.findProtocol("MPING"); + if(dynamic_discovery_prot == null) + dynamic_discovery_prot=stack.findProtocol("TCPGOSSIP"); + + if(dynamic_discovery_prot != null) { + if(log.isDebugEnabled()) + log.debug("dynamic discovery is present (" + dynamic_discovery_prot + "), so start_port=" + start_port + " is okay"); + } + else { + throw new IllegalArgumentException("start_port cannot be set to " + start_port + + ", as no dynamic discovery protocol (e.g. MPING or TCPGOSSIP) has been detected."); + } + } + } + + + + public void sendToAllMembers(byte[] data, int offset, int length) throws Exception { + Set
mbrs; + + synchronized(members) { + mbrs=(Set
)members.clone(); + } + for(Address dest: mbrs) { + sendToSingleMember(dest, data, offset, length); + } + } + + public void sendToSingleMember(Address dest, byte[] data, int offset, int length) throws Exception { + if(log.isTraceEnabled()) log.trace("dest=" + dest + " (" + length + " bytes)"); + if(skip_suspected_members) { + if(suspected_mbrs.contains(dest)) { + if(log.isTraceEnabled()) + log.trace("will not send unicast message to " + dest + " as it is currently suspected"); + return; + } + } + + try { + send(dest, data, offset, length); + } + catch(Exception e) { + if(log.isTraceEnabled()) + log.trace("failure sending message to " + dest, e); + if(suspect_on_send_failure && members.contains(dest)) { + if(!suspected_mbrs.contains(dest)) { + suspected_mbrs.add(dest); + up_prot.up(new Event(Event.SUSPECT, dest)); + } + } + } + } + + public String getInfo() { + StringBuilder sb=new StringBuilder(); + sb.append("connections: ").append(printConnections()).append("\n"); + return sb.toString(); + } + + public void postUnmarshalling(Message msg, Address dest, Address src, boolean multicast) { + if(multicast) + msg.setDest(null); + else + msg.setDest(dest); + } + + public void postUnmarshallingList(Message msg, Address dest, boolean multicast) { + postUnmarshalling(msg, dest, null, multicast); + } + + public abstract String printConnections(); + + public abstract void send(Address dest, byte[] data, int offset, int length) throws Exception; + + public abstract void retainAll(Collection
members); + + /** ConnectionTable.Receiver interface */ + public void receive(Address sender, byte[] data, int offset, int length) { + receive(local_addr, sender, data, offset, length); + } + + protected Object handleDownEvent(Event evt) { + Object ret=super.handleDownEvent(evt); + if(evt.getType() == Event.VIEW_CHANGE) { + suspected_mbrs.clear(); + retainAll(members); // remove all connections from the ConnectionTable which are not members + } + else if(evt.getType() == Event.UNSUSPECT) { + Address suspected_mbr=(Address)evt.getArg(); + suspected_mbrs.remove(suspected_mbr); + } + return ret; + } +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/CAUSAL.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/CAUSAL.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/CAUSAL.java 17 Aug 2012 14:51:07 -0000 1.1 @@ -0,0 +1,1067 @@ + +package org.jgroups.protocols; + +import org.jgroups.*; +import org.jgroups.stack.Protocol; +import org.jgroups.util.Streamable; + +import java.io.*; +import java.util.*; + +/**

+ * Implements casual ordering layer using vector clocks. + *

+ *

+ * Causal protocol layer guarantees that if message m0 multicasted + * by a process group member p0 causes process group member + * p1 to multicast message m1 then all other remaining process group + * members in a current view will receive messages in order m0 + * followed by m1. + *

+ *

+ * First time encountered, causal order seems very similar to FIFO order but + * there is an important distinction. While FIFO order gurantees that + * if process group member p0 multicasts m0 followed by m1 the messages + * will be delivered in order m0,m1 to all other group members, causal + * order expands this notion of an order from a single group member "space" + * to a whole group space i.e if p0 sends message m0 which causes member + * p1 to send message m1 then all other group members are guaranteed to + * receive m0 followed by m1. + *

+ *

+ * Causal protocol layer achieves this ordering type by introducing sense of + * a time in a group using vector clocks. The idea is very simple. Each message + * is labeled by a vector, contained in a causal header, representing the number of + * prior causal messages received by the sending group member. Vector time of [3,5,2,4] in + * a group of four members [p0,p1,p2,p3] means that process p0 has sent 3 messages + * and has received 5,2 and 4 messages from a member p1,p2 and p3 respectively. + *

+ *

+ * Each member increases its counter by 1 when it sends a message. When receiving + * message mi from a member pi , (where pi != pj) containing vector time VT(mi), + * process pj delays delivery of a message mi until: + *

+ *

+ * for every k:1..n + * + * VT(mi)[k] == VT(pj)[k] + 1 if k=i, + * VT(mi)[k] <= VT(pj)[k] otherwise + *

+ *

+ * After the next causal message is delivered at process group pj, VT(pj) is + * updated as follows: + *

+ *

+ * for every k:1...n VT(pj)[k] == max(VT(mi)[k],VT(pj)[k]) + *

+ * @author Vladimir Blagojevic vladimir@cs.yorku.ca + * @version $Id: CAUSAL.java,v 1.1 2012/08/17 14:51:07 marcin Exp $ + * + **/ + + +public class CAUSAL extends Protocol +{ + + public static final class CausalHeader extends Header implements Streamable { + + /** + * Comment for serialVersionUID + */ + private static final long serialVersionUID = 3760846744526927667L; + + /** + * vector timestamp of this header/message + */ + private TransportedVectorTime t = null; + + /** + *used for externalization + */ + public CausalHeader() { + } + + public CausalHeader(TransportedVectorTime timeVector) { + t = timeVector; + } + + /** + *Returns a vector timestamp carreid by this header + *@return Vector timestamp contained in this header + */ + public TransportedVectorTime getVectorTime() { + return t; + } + + /** + * Size of this vector timestamp estimation, used in fragmetation + * @return headersize in bytes + */ + public int size() + { + int retval=Global.BYTE_SIZE; + if(t == null) + return retval; + retval+=t.senderPosition; + if(t.values != null) { + retval+=t.values.length * Global.INT_SIZE; + } + return retval; + } + + /** + * Manual serialization + */ + public void writeExternal(ObjectOutput out) throws IOException + { + out.writeObject(t); + } + + /** + * Manual deserialization + */ + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException + { + t = (TransportedVectorTime) in.readObject(); + } + + public void writeTo(DataOutputStream out) throws IOException { + if(t == null) + { + out.writeBoolean(false); + return; + } + out.writeBoolean(true); + + out.writeInt(t.senderPosition); + + int values[]=t.values; + + int len=values.length; + out.writeInt(len); + for(int i=0;i=len) + throw new InstantiationException("sender position="+t.senderPosition+", values length="+len); + + t.values=new int[len]; + for(int i=0;iserialVersionUID. + */ + private static final long serialVersionUID = 3257569486185183289L; + + public final static String NAME="CAUSAL_NEWVIEW_HEADER"; + + /** + * New view id. + */ + private ViewId newViewId; + + /** + * Sender local time. + */ + private int localTime; + + private boolean complete; + + public CausalNewViewHeader(ViewId newViewId, int localTime, boolean complete) { + this.newViewId=newViewId; + this.localTime=localTime; + this.complete=complete; + } + + /** + * Used for externalization. + */ + public CausalNewViewHeader() { + } + + public ViewId getNewViewId() { + return newViewId; + } + + public int getLocalTime() { + return localTime; + } + + public boolean isComplete() { + return complete; + } + + /** + * Size of this vector timestamp estimation, used in fragmentation. + * @return headersize in bytes + */ + public int size() { + /*why 231, don't know but these are this values I get when + flattening the object into byte buffer*/ + return 231; + } + + /** + * Manual serialization + */ + public void writeExternal(ObjectOutput out) throws IOException { + out.writeObject(newViewId); + out.writeInt(localTime); + out.writeBoolean(complete); + } + + /** + * Manual deserialization + */ + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + newViewId=(ViewId)in.readObject(); + localTime=in.readInt(); + complete=in.readBoolean(); + } + + public void writeTo(DataOutputStream out) throws IOException { + newViewId.writeTo(out); + out.writeInt(localTime); + out.writeBoolean(complete); + } + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + newViewId=new ViewId(); + newViewId.readFrom(in); + localTime=in.readInt(); + complete=in.readBoolean(); + } + + public String toString() { + return '[' + NAME + ':' + newViewId + "; @" + localTime + (complete?"; complete]":"; incomplete]"); + } + + } + + public static final class MissingIndexesMessage implements Externalizable, Streamable { + + /** + * Comment for serialVersionUID + */ + private static final long serialVersionUID = 3257007644266213432L; + + /** + * Member indexes the sender is waiting the local time from. + */ + private int missingTimeIndexes[]; + + /** + * Member indexes the sender is waiting the completion from. + */ + private int missingCompletionIndexes[]; + + public MissingIndexesMessage(Collection missingLocalTimes, Collection missingCompletions) { + missingTimeIndexes=new int[missingLocalTimes.size()]; + int i=0; + for(Iterator it=missingLocalTimes.iterator();it.hasNext();) + missingTimeIndexes[i++]=((Integer)it.next()).intValue(); + + missingCompletionIndexes=new int[missingCompletions.size()]; + i=0; + for(Iterator it=missingCompletions.iterator();it.hasNext();) + missingCompletionIndexes[i++]=((Integer)it.next()).intValue(); + } + + /** + * Used for externalization. + */ + public MissingIndexesMessage() { + } + + public int[] getMissingTimeIndexes() { + return missingTimeIndexes; + } + + public int[] getMissingCompletionIndexes() { + return missingCompletionIndexes; + } + + private static void writeIntArray(DataOutput out, int array[]) throws IOException { + out.writeInt(array.length); + for(int i=0;i0) sb.append(", "); + sb.append(i).append(':').append(members[i]); + } + sb.append(" - local is ").append(localIndex); + return sb.toString(); + } + + } + + private final class NewCausalView { + + private final ActiveCausalView active; + + private final InternalView view; + + private final int timeVector[]; + + private final TreeSet missingTimes=new TreeSet(), missingCompletions=new TreeSet(); + + public NewCausalView(ActiveCausalView active, InternalView view) { + this.active=active; + this.view=view; + + // Setup time vector + timeVector=new int[view.size()]; + + if (view.getLocalIndex()>=timeVector.length) + throw new IllegalStateException("View: "+view+" timevector.length="+timeVector.length); + + for(int i=0;i=initialTimeVector.length) + throw new IllegalStateException("View: "+view+" timevector.length="+initialTimeVector.length); + } + + public InternalView getView() { + return view; + } + + public ViewId getViewId() { + return view.getViewId(); + } + + public int getLocalIndex() { + return view.getLocalIndex(); + } + + public synchronized int getLocalTime() { + return timeVector[view.getLocalIndex()]; + } + + /** + * Increment local time. + */ + public synchronized void increment() { + timeVector[view.getLocalIndex()]++; + } + + /** + * Returns a minimal lightweight representation of this Vector Time + * suitable for network transport. + * @return lightweight representation of this VectorTime in the + * form of TransportedVectorTime object + */ + public synchronized TransportedVectorTime getTransportedVectorTime() { + // Need to make a copy of the time vector + int tmpTimeVector[]=new int[this.timeVector.length]; + System.arraycopy(this.timeVector, 0, tmpTimeVector, 0, tmpTimeVector.length); + + return new TransportedVectorTime(view.getLocalIndex(), tmpTimeVector); + } + + public synchronized boolean isCausallyNext(TransportedVectorTime vector) { + int senderIndex = vector.getSenderIndex(); + + if (senderIndex == view.getLocalIndex()) return true; + + int[] otherTimeVector = vector.getValues(); + + if (otherTimeVector.length!=timeVector.length) { + if(log.isWarnEnabled()) + log.warn("isCausallyNext: got message with wrong time vector length: "+otherTimeVector.length+ + ", expected: "+timeVector.length); + return true; + } + + boolean nextCausalFromSender = false; + boolean nextCausal = true; + + for (int i=0;i timeVector[i]) nextCausal = false; + } + + return (nextCausalFromSender && nextCausal); + } + + public synchronized void max(TransportedVectorTime vector) { + int otherTimeVector[]=vector.getValues(); + + if (otherTimeVector.length!=timeVector.length) { + if(log.isWarnEnabled()) + log.warn("max: got message with wrong time vector length: "+otherTimeVector.length+", expected: "+ + timeVector.length); + return; + } + + for(int i=0;itimeVector[i]) timeVector[i]=otherTimeVector[i]; + } + } + + public synchronized void setFinalTimeVector(InternalView newView, int startTimeVector[]) { + finalTimeVector=new int[timeVector.length]; + System.arraycopy(timeVector, 0, finalTimeVector, 0, timeVector.length); + + for(int i=0;i0) sb.append(", "); + sb.append(timeVector[i]); + } + return sb.toString(); + } + + public String toString() { + return "ActiveCausalView["+view+']'; + } + + } + + private final class NewViewThread extends Thread { + + private final NewCausalView newView; + + private boolean updateRequested=false; + + NewViewThread(NewCausalView newView) { + super(newView.getViewId().toString()); + this.newView=newView; + setDaemon(true); + } + + NewCausalView getCausalView() { + return newView; + } + + public void run() { + boolean sendUpdate=false, complete=false; + LinkedList flush=null; + + for(;;) { + Message update=null; + + synchronized(lock) { + if(this != newViewThread) { + break; + } + + if(newView.hasMissingTimes()) { + sendUpdate=true; + } + else + if(currentView == null || (!currentView.getViewId().equals(newView.getViewId()) && currentView.hasEnded())) + { + currentView=new ActiveCausalView(newView.getView(), newView.timeVector); + complete=true; + newView.setMemberCompleted(localAddress); + + if(log.isTraceEnabled()) + log.trace("Set up new active view: " + currentView + " @ " + currentView.timeVectorString()); + } + + if(newView.hasMissingCompletions()) { + sendUpdate=true; + } + else { + newViewThread=null; + enabled=true; + flush=(LinkedList)downwardWaitingQueue.clone(); + downwardWaitingQueue.clear(); + + if(log.isTraceEnabled()) + log.trace("Done synchronizing, enabled view: " + currentView + " @ " + currentView.timeVectorString()); + + break; + } + + if(sendUpdate) { + update=new Message(null, localAddress, null); + update.putHeader(CausalNewViewHeader.NAME + , new CausalNewViewHeader(newView.getViewId(), newView.getLocalTime(), complete)); + update.setObject(new MissingIndexesMessage(newView.getMissingTimes(), newView.getMissingCompletions())); + } + } + + if(update != null) { + if(log.isTraceEnabled()) + log.trace("Sending sync update"); + down_prot.down(new Event(Event.MSG, update)); + } + + synchronized(this) { + // Wait for 50ms + try { + wait(500); + } + catch(InterruptedException e) { + Thread.currentThread().interrupt(); // set interrupt flag again + // Ignore + log.warn("Interrupted?!?", e); + } + + sendUpdate=updateRequested; + updateRequested=false; + } + } + + if (flush!=null) { + int n=flush.size(); + if (log.isDebugEnabled()) log.debug("Flushing "+n+" messages down..."); + + while(!flush.isEmpty()) { + Event evt=(Event)flush.removeFirst(); + down(evt); + } + + if (log.isDebugEnabled()) log.debug("Done flushing "+n+" messages down..."); + } + } + + void updateRequested() { + synchronized(this) { + updateRequested=true; + } + } + + } + + + private final Object lock=new Object(); + + /** + * Local address. + */ + private Address localAddress; + + /** + * Queue containing upward messages waiting for delivery i.e causal order. + */ + private final LinkedList upwardWaitingQueue=new LinkedList(); + + /** + * Queue containing downward messages waiting for sending. + */ + private final LinkedList downwardWaitingQueue=new LinkedList(); + + private boolean enabled=false; + + /** + * The active view (including its time vector), if any. + */ + private ActiveCausalView currentView; + + private NewViewThread newViewThread; + + private boolean debug=false; + + /** + * Default constructor. + */ + public CAUSAL() { + } + + public boolean setProperties(Properties props) { + if (!super.setProperties(props)) return false; + + String s=props.getProperty("debug"); + debug="debug".equalsIgnoreCase(s); + + return true; + } + + /** + * Adds a vectortimestamp to a sorted queue + * @param tvt A vector time stamp + */ + private void addToDelayQueue(TransportedVectorTime tvt) + { + ListIterator i = upwardWaitingQueue.listIterator(0); + TransportedVectorTime current = null; + while (i.hasNext()) + { + current = (TransportedVectorTime) i.next(); + if (tvt.lessThanOrEqual(current)) + { + upwardWaitingQueue.add(i.previousIndex(), tvt); + return; + } + } + upwardWaitingQueue.add(tvt); + } + + // Must be called sync'd on lock + private void disable() { + enabled=false; + } + + // Must be called sync'd on lock + private boolean isEnabled() { + return enabled; + } + +// // Must be called sync'd on lock +// private boolean tryEnable() { +// if (currentView!=null && !currentView.hasEnded()) return false; +// +// currentView=new ActiveCausalView(newView.getView(), newView.timeVector); +// newView=null; +// enabled=true; +// +// // FIXME send all waiting messages. +// +// return true; +// } + + /** + * Process a downward event. + * @param evt The event. + */ + public Object down(Event evt) { + try { + // If not a MSG, just pass down. + if (evt.getType()!=Event.MSG) { + return down_prot.down(evt); + } + + Message msg = (Message) evt.getArg(); + + // If unicast, just pass down. + if (msg.getDest()!=null && ! msg.getDest().isMulticastAddress()) { + return down_prot.down(evt); + } + + // Multicast MSG: + // - if enabled, get the next time vector, add it and pass down; + // - otherwise, add to the downward waiting queue. + TransportedVectorTime tvt=null; + + synchronized(lock) { + if (isEnabled()) { + currentView.increment(); + tvt=currentView.getTransportedVectorTime(); + if (log.isTraceEnabled()) log.trace("Sent 1 down message @ "+currentView.timeVectorString()); + } else { + if (log.isTraceEnabled()) log.trace("Enqueued 1 down message..."); + downwardWaitingQueue.add(evt); + } + } + + if (tvt!=null) { + msg.putHeader(getName(), new CausalHeader(tvt)); + return down_prot.down(evt); + } + } catch (RuntimeException e) { + if (debug) log.error("*** down: "+e.getMessage(), e); + throw e; + } + + return null; + } + + /** + * Process an upward event. + * @param evt The event. + */ + public Object up(Event evt) { + try { + switch (evt.getType()) { + case Event.SET_LOCAL_ADDRESS: + upSetLocalAddress(evt); + break; + case Event.VIEW_CHANGE: + upViewChange(evt); + break; + case Event.MSG: + return upMsg(evt); + default: + return up_prot.up(evt); + } + } catch (RuntimeException e) { + if (debug) log.error("*** up: "+e.getMessage(), e); + throw e; + } + return null; + } + + private void upSetLocalAddress(Event evt) { + localAddress = (Address) evt.getArg(); + up_prot.up(evt); + } + + /** + * Process a VIEW_CHANGE event. + * @param evt The event. + */ + private void upViewChange(Event evt) + { + View view=(View)evt.getArg(); + InternalView iView=new InternalView(view.getVid(), view.getMembers(), localAddress); + if(log.isDebugEnabled()) + log.debug("New view: "+view); + + synchronized(lock) { + // Disable sending + disable(); + + // Create new causal view + NewCausalView newView=new NewCausalView(currentView, iView); + if (currentView!=null) { + currentView.clearFinalTimeVector(); + newView.setMemberLocalTime(localAddress, currentView.getLocalTime()); + } else { + newView.setMemberLocalTime(localAddress, 0); + } + + if (log.isTraceEnabled()) log.trace("Starting synchronization thread for "+newView); + + newViewThread=new NewViewThread(newView); + newViewThread.start(); + } + + up_prot.up(evt); + } + + private Object upMsg(Event evt) { + Message msg = (Message) evt.getArg(); + Address src=msg.getSrc(); + + // Check for a causal new view header + Object obj = msg.getHeader(CausalNewViewHeader.NAME); + + if (obj instanceof CausalNewViewHeader) { + processNewViewSynchronization(src, (CausalNewViewHeader)obj, msg.getObject()); + return null; + } + + obj = msg.getHeader(getName()); + + if (!(obj instanceof CausalHeader)) { + if((msg.getDest() == null || msg.getDest().isMulticastAddress()) + && log.isErrorEnabled()) log.error("NO CAUSAL.Header found"); + return up_prot.up(evt); + } + + TransportedVectorTime messageVector = ((CausalHeader)obj).getVectorTime(); + + synchronized (lock) { + if (currentView==null||currentView.getView().getIndex(src)<0) { + if (log.isDebugEnabled()) log.debug("Discarding "+obj+" from "+msg.getSrc()); + return null; + } + + if (currentView.isCausallyNext(messageVector)) { + if (log.isTraceEnabled()) log.trace("passing up message "+msg+", headers are "+msg.printHeaders()+", local vector is "+currentView.timeVectorString()); + up_prot.up(evt); + currentView.max(messageVector); + } else { + if (log.isTraceEnabled()) log.trace("queuing message "+msg+", headers are "+msg.printHeaders()); + messageVector.setAssociatedMessage(msg); + addToDelayQueue(messageVector); + } + + TransportedVectorTime queuedVector = null; + + while ((!upwardWaitingQueue.isEmpty()) && + currentView.isCausallyNext((queuedVector = (TransportedVectorTime) upwardWaitingQueue.getFirst()))) { + upwardWaitingQueue.remove(queuedVector); + Message tmp=queuedVector.getAssociatedMessage(); + if (log.isTraceEnabled()) log.trace("released message "+tmp+", headers are "+tmp.printHeaders()); + up_prot.up(new Event(Event.MSG, tmp)); + currentView.max(queuedVector); + } + } + return null; + } + + /** + * + */ + private void processNewViewSynchronization(Address src, CausalNewViewHeader header, Object object) { + // If from ourselves, ignore. + if (localAddress.equals(src)) return; + + MissingIndexesMessage content=(MissingIndexesMessage)object; + + if(log.isTraceEnabled()) + log.trace("Got sync update from "+src); + + synchronized(lock) { + if (newViewThread==null) { + if (currentView!=null&¤tView.getView().getViewId().equals(header.newViewId)) { + // Somebody's late... + int localIndex=currentView.getLocalIndex(); + + if (Arrays.binarySearch(content.getMissingCompletionIndexes(), localIndex)>=0) { + Message update=new Message(null, localAddress, null); + update.putHeader(CausalNewViewHeader.NAME + , new CausalNewViewHeader(currentView.getView().getViewId(), 0, true)); // It has the time already + update.setObject(new MissingIndexesMessage(Collections.EMPTY_LIST, Collections.EMPTY_LIST)); + + down_prot.down(new Event(Event.MSG, update)); + } + + if (Arrays.binarySearch(content.getMissingTimeIndexes(), localIndex)>=0) { + + } + } else { + // Somebody's early... + disable(); + } + return; + } + + if (!newViewThread.getCausalView().getViewId().equals(header.newViewId)) return; + + if (log.isTraceEnabled()) log.trace("From "+src+": "+header); + + // Update the local time and completion status for the source. + newViewThread.getCausalView().setMemberLocalTime(src, header.localTime); + if (header.isComplete()) newViewThread.getCausalView().setMemberCompleted(src); + + // Check the requested times and completions + int localIndex=newViewThread.getCausalView().getLocalIndex(); + + if ( Arrays.binarySearch(content.getMissingTimeIndexes(), localIndex)>=0 + || Arrays.binarySearch(content.getMissingCompletionIndexes(), localIndex)>=0 ) { + newViewThread.updateRequested(); + } + } + } + + /** + * Returns a name of this stack, each stackhas to have unique name + * @return stack's name - CAUSAL + */ + public String getName() { + return "CAUSAL"; + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/COMPRESS.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/COMPRESS.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/COMPRESS.java 17 Aug 2012 14:51:06 -0000 1.1 @@ -0,0 +1,229 @@ +package org.jgroups.protocols; + +import org.jgroups.Event; +import org.jgroups.Global; +import org.jgroups.Header; +import org.jgroups.Message; +import org.jgroups.stack.Protocol; +import org.jgroups.util.Streamable; + +import java.io.*; +import java.util.Properties; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.zip.DataFormatException; +import java.util.zip.Deflater; +import java.util.zip.Inflater; + +/** + * Compresses the payload of a message. Goal is to reduce the number of messages sent across the wire. + * Should ideally be layered somewhere above a fragmentation protocol (e.g. FRAG). + * @author Bela Ban + * @version $Id: COMPRESS.java,v 1.1 2012/08/17 14:51:06 marcin Exp $ + */ +public class COMPRESS extends Protocol { + BlockingQueue deflater_pool=null; + BlockingQueue inflater_pool=null; + + + /** Values are from 0-9 (0=no compression, 9=best compression) */ + int compression_level=Deflater.BEST_COMPRESSION; // this is 9 + + /** Minimal payload size of a message (in bytes) for compression to kick in */ + long min_size=500; + + /** Number of inflaters/deflaters, for concurrency, increase this to the max number of concurrent requests possible */ + int pool_size=2; + + + final static String name="COMPRESS"; + + public String getName() { + return name; + } + + + public void init() throws Exception { + deflater_pool=new ArrayBlockingQueue(pool_size); + for(int i=0; i < pool_size; i++) { + deflater_pool.add(new Deflater(compression_level)); + } + inflater_pool=new ArrayBlockingQueue(pool_size); + for(int i=0; i < pool_size; i++) { + inflater_pool.add(new Inflater()); + } + } + + public void destroy() { + for(Deflater deflater: deflater_pool) + deflater.end(); + for(Inflater inflater: inflater_pool) + inflater.end(); + } + + + public boolean setProperties(Properties props) { + String str; + + super.setProperties(props); + str=props.getProperty("compression_level"); + if(str != null) { + compression_level=Integer.parseInt(str); + props.remove("compression_level"); + } + + str=props.getProperty("min_size"); + if(str != null) { + min_size=Long.parseLong(str); + props.remove("min_size"); + } + + str=props.getProperty("pool_size"); + if(str != null) { + pool_size=Integer.parseInt(str); + if(pool_size <= 0) { + log.warn("pool_size must be > 0, setting it to 1"); + pool_size=1; + } + props.remove("pool_size"); + } + + if(!props.isEmpty()) { + log.error("the following properties are not recognized: " + props); + return false; + } + return true; + } + + + + /** + * We compress the payload if it is larger than min_size. In this case we add a header containing + * the original size before compression. Otherwise we add no header.
+ * Note that we compress either the entire buffer (if offset/length are not used), or a subset (if offset/length + * are used) + * @param evt + */ + public Object down(Event evt) { + if(evt.getType() == Event.MSG) { + Message msg=(Message)evt.getArg(); + int length=msg.getLength(); // takes offset/length (if set) into account + if(length >= min_size) { + byte[] payload=msg.getRawBuffer(); // here we get the ref so we can avoid copying + byte[] compressed_payload=new byte[length]; + int compressed_size; + Deflater deflater=null; + try { + deflater=deflater_pool.take(); + deflater.reset(); + deflater.setInput(payload, msg.getOffset(), length); + deflater.finish(); + deflater.deflate(compressed_payload); + compressed_size=deflater.getTotalOut(); + byte[] new_payload=new byte[compressed_size]; + System.arraycopy(compressed_payload, 0, new_payload, 0, compressed_size); + msg.setBuffer(new_payload); + msg.putHeader(name, new CompressHeader(length)); + if(log.isTraceEnabled()) + log.trace("compressed payload from " + length + " bytes to " + compressed_size + " bytes"); + } + catch(InterruptedException e) { + Thread.currentThread().interrupt(); // set interrupt flag again + throw new RuntimeException(e); + } + finally { + if(deflater != null) + deflater_pool.offer(deflater); + } + } + } + return down_prot.down(evt); + } + + + + /** + * If there is no header, we pass the message up. Otherwise we uncompress the payload to its original size. + * @param evt + */ + public Object up(Event evt) { + if(evt.getType() == Event.MSG) { + Message msg=(Message)evt.getArg(); + CompressHeader hdr=(CompressHeader)msg.getHeader(name); + if(hdr != null) { + byte[] compressed_payload=msg.getRawBuffer(); + if(compressed_payload != null && compressed_payload.length > 0) { + int original_size=hdr.original_size; + byte[] uncompressed_payload=new byte[original_size]; + Inflater inflater=null; + try { + inflater=inflater_pool.take(); + inflater.reset(); + inflater.setInput(compressed_payload, msg.getOffset(), msg.getLength()); + try { + inflater.inflate(uncompressed_payload); + if(log.isTraceEnabled()) + log.trace("uncompressed " + compressed_payload.length + " bytes to " + original_size + + " bytes"); + // we need to copy: https://jira.jboss.org/jira/browse/JGRP-867 + Message copy=msg.copy(false); + copy.setBuffer(uncompressed_payload); + return up_prot.up(new Event(Event.MSG, copy)); + // msg.setBuffer(uncompressed_payload); + } + catch(DataFormatException e) { + if(log.isErrorEnabled()) log.error("exception on uncompression", e); + } + } + catch(InterruptedException e) { + Thread.currentThread().interrupt(); // set the interrupt bit again, so caller can handle it + } + finally { + if(inflater != null) + inflater_pool.offer(inflater); + } + + } + } + } + return up_prot.up(evt); + } + + + + + + + public static class CompressHeader extends Header implements Streamable { + int original_size=0; + + public CompressHeader() { + super(); + } + + public CompressHeader(int s) { + original_size=s; + } + + + public int size() { + return Global.INT_SIZE; + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeInt(original_size); + } + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + original_size=in.readInt(); + } + + public void writeTo(DataOutputStream out) throws IOException { + out.writeInt(original_size); + } + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + original_size=in.readInt(); + } + } +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/DELAY.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/DELAY.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/DELAY.java 17 Aug 2012 14:51:08 -0000 1.1 @@ -0,0 +1,95 @@ +// $Id: DELAY.java,v 1.1 2012/08/17 14:51:08 marcin Exp $ + +package org.jgroups.protocols; + +import org.jgroups.Event; +import org.jgroups.stack.Protocol; +import org.jgroups.util.Util; + +import java.util.Properties; + + +/** + * Delays incoming/outgoing messages by a random number of milliseconds (range between 0 and n + * where n is determined by the user). Incoming messages can be delayed independently from + * outgoing messages (or not delayed at all).

+ * This protocol should be inserted directly above the bottommost protocol (e.g. UDP). + */ + +public class DELAY extends Protocol { + int in_delay=0, out_delay=0; + + /** + * All protocol names have to be unique ! + */ + public String getName() { + return "DELAY"; + } + + + public boolean setProperties(Properties props) { + String str; + + super.setProperties(props); + + str=props.getProperty("in_delay"); + if(str != null) { + in_delay=Integer.parseInt(str); + props.remove("in_delay"); + } + + str=props.getProperty("out_delay"); + if(str != null) { + out_delay=Integer.parseInt(str); + props.remove("out_delay"); + } + + if(!props.isEmpty()) { + log.error("DELAY.setProperties(): these properties are not recognized: " + props); + return false; + } + return true; + } + + + public Object up(Event evt) { + int delay=in_delay > 0 ? computeDelay(in_delay) : 0; + + + switch(evt.getType()) { + case Event.MSG: // Only delay messages, not other events ! + + if(log.isInfoEnabled()) log.info("delaying incoming message for " + delay + " milliseconds"); + Util.sleep(delay); + break; + } + + return up_prot.up(evt); // Pass up to the layer above us + } + + + public Object down(Event evt) { + int delay=out_delay > 0 ? computeDelay(out_delay) : 0; + + switch(evt.getType()) { + + case Event.MSG: // Only delay messages, not other events ! + + if(log.isInfoEnabled()) log.info("delaying outgoing message for " + delay + " milliseconds"); + Util.sleep(delay); + break; + } + + return down_prot.down(evt); // Pass on to the layer below us + } + + + /** + * Compute a random number between 0 and n + */ + static int computeDelay(int n) { + return (int)((Math.random() * 1000000) % n); + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/DELAY_JOIN_REQ.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/DELAY_JOIN_REQ.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/DELAY_JOIN_REQ.java 17 Aug 2012 14:51:07 -0000 1.1 @@ -0,0 +1,75 @@ +package org.jgroups.protocols; + +import org.jgroups.Event; +import org.jgroups.Message; +import org.jgroups.protocols.pbcast.GMS; +import org.jgroups.stack.Protocol; +import org.jgroups.util.Util; + +import java.util.Date; +import java.util.Properties; + +/** + * Discards 2 JOIN-REQs then accepts 1, then discards 2 more and so on + * @author Bela Ban + * @version $Id: DELAY_JOIN_REQ.java,v 1.1 2012/08/17 14:51:07 marcin Exp $ + */ +public class DELAY_JOIN_REQ extends Protocol { + private long delay=4000; + + public String getName() { + return "DELAY_JOIN_REQ"; + } + + public boolean setProperties(Properties props) { + String str; + + super.setProperties(props); + + str=props.getProperty("delay"); + if(str != null) { + delay=Long.parseLong(str); + props.remove("delay"); + } + + if(!props.isEmpty()) { + log.error("these properties are not recognized: " + props); + return false; + } + return true; + } + + public long getDelay() { + return delay; + } + + public void setDelay(long delay) { + this.delay=delay; + } + + public Object up(final Event evt) { + switch(evt.getType()) { + case Event.MSG: + Message msg=(Message)evt.getArg(); + final GMS.GmsHeader hdr=(GMS.GmsHeader)msg.getHeader("GMS"); + if(hdr != null) { + switch(hdr.getType()) { + case GMS.GmsHeader.JOIN_REQ: + case GMS.GmsHeader.JOIN_REQ_WITH_STATE_TRANSFER: + System.out.println(new Date() + ": delaying JOIN-REQ by " + delay + " ms"); + Thread thread=new Thread() { + public void run() { + Util.sleep(delay); + System.out.println(new Date() + ": sending up delayed JOIN-REQ by " + hdr.getMember()); + up_prot.up(evt); + } + }; + thread.start(); + return null; + } + } + break; + } + return up_prot.up(evt); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/DISCARD.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/DISCARD.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/DISCARD.java 17 Aug 2012 14:51:06 -0000 1.1 @@ -0,0 +1,434 @@ +// $Id: DISCARD.java,v 1.1 2012/08/17 14:51:06 marcin Exp $ + +package org.jgroups.protocols; + +import org.jgroups.*; +import org.jgroups.Event; +import org.jgroups.stack.Protocol; +import org.jgroups.util.Streamable; +import org.jgroups.util.Util; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.*; +import java.util.*; + + +/** + * Discards up or down messages based on a percentage; e.g., setting property 'up' to 0.1 causes 10% + * of all up messages to be discarded. Setting 'down' or 'up' to 0 causes no loss, whereas 1 discards + * all messages (not very useful). + */ + +public class DISCARD extends Protocol { + double up=0.0; // probability of dropping up msgs + double down=0.0; // probability of dropping down msgs + boolean excludeItself=true; // if true don't discard messages sent/received in this stack + Address localAddress; + int num_down=0, num_up=0; + + final Set

ignoredMembers = new HashSet
(); + boolean discard_all=false; + + // number of subsequent unicasts to drop in the down direction + int drop_down_unicasts=0; + + // number of subsequent multicasts to drop in the down direction + int drop_down_multicasts=0; + + private DiscardDialog discard_dialog=null; + + protected boolean use_gui=false; + + + /** + * All protocol names have to be unique ! + */ + public String getName() { + return "DISCARD"; + } + + + public boolean isExcludeItself() { + return excludeItself; + } + + public void setLocalAddress(Address localAddress){ + this.localAddress =localAddress; + if(discard_dialog != null) + discard_dialog.setTitle(localAddress != null? localAddress.toString() : "n/a"); + } + + public void setExcludeItself(boolean excludeItself) { + this.excludeItself=excludeItself; + } + + public double getUpDiscardRate() { + return up; + } + + public void setUpDiscardRate(double up) { + this.up=up; + } + + public double getDownDiscardRate() { + return down; + } + + public void setDownDiscardRate(double down) { + this.down=down; + } + + public int getDropDownUnicasts() { + return drop_down_unicasts; + } + + /** + * Drop the next N unicasts down the stack + * @param drop_down_unicasts + */ + public void setDropDownUnicasts(int drop_down_unicasts) { + this.drop_down_unicasts=drop_down_unicasts; + } + + public int getDropDownMulticasts() { + return drop_down_multicasts; + } + + public void setDropDownMulticasts(int drop_down_multicasts) { + this.drop_down_multicasts=drop_down_multicasts; + } + + /** Messages from this sender will get dropped */ + public void addIgnoreMember(Address sender) {ignoredMembers.add(sender);} + + public void removeIgnoredMember(Address member) {ignoredMembers.remove(member);} + + public void resetIgnoredMembers() {ignoredMembers.clear();} + + + + + public boolean setProperties(Properties props) { + String str; + + super.setProperties(props); + str=props.getProperty("up"); + if(str != null) { + up=Double.parseDouble(str); + props.remove("up"); + } + + str=props.getProperty("down"); + if(str != null) { + down=Double.parseDouble(str); + props.remove("down"); + } + + str=props.getProperty("excludeitself"); + if(str != null) { + excludeItself=Boolean.valueOf(str).booleanValue(); + props.remove("excludeitself"); + } + + str=props.getProperty("use_gui"); + if(str != null) { + use_gui=Boolean.valueOf(str).booleanValue(); + props.remove("use_gui"); + } + + if(!props.isEmpty()) { + log.error("DISCARD.setProperties(): these properties are not recognized: " + props); + return false; + } + return true; + } + + + + + public void start() throws Exception { + super.start(); + if(use_gui) { + discard_dialog=new DiscardDialog(); + discard_dialog.init(); + } + } + + public void stop() { + super.stop(); + if(discard_dialog != null) + discard_dialog.dispose(); + } + + public Object up(Event evt) { + Message msg; + double r; + + if(evt.getType() == Event.SET_LOCAL_ADDRESS) { + localAddress=(Address)evt.getArg(); + if(discard_dialog != null) + discard_dialog.setTitle(localAddress != null? localAddress.toString() : "n/a"); + } + + if(evt.getType() == Event.MSG) { + msg=(Message)evt.getArg(); + Address sender=msg.getSrc(); + + if(discard_all && !sender.equals(localAddress)) { + return null; + } + + DiscardHeader dh = (DiscardHeader) msg.getHeader(getName()); + if (dh != null) { + ignoredMembers.clear(); + ignoredMembers.addAll(dh.dropMessages); + if (log.isTraceEnabled()) + log.trace("will potentially drop messages from " + ignoredMembers); + } else { + boolean dropMessage=ignoredMembers.contains(sender); + if (dropMessage) { + if (log.isTraceEnabled()) + log.trace("dropping message from " + sender); + num_up++; + return null; + } + + if (up > 0) { + r = Math.random(); + if (r < up) { + if (excludeItself && sender.equals(localAddress)) { + if (log.isTraceEnabled()) + log.trace("excluding itself"); + } else { + if (log.isTraceEnabled()) + log.trace("dropping message from " + sender); + num_up++; + return null; + } + } + } + } + } + + return up_prot.up(evt); + } + + + public Object down(Event evt) { + Message msg; + double r; + + switch(evt.getType()) { + case Event.MSG: + msg=(Message)evt.getArg(); + Address dest=msg.getDest(); + boolean multicast=dest == null || dest.isMulticastAddress(); + + if(msg.getSrc() == null) + msg.setSrc(localAddress); + + if(discard_all) { + if(dest == null || dest.isMulticastAddress() || dest.equals(localAddress)) { + //System.out.println("[" + localAddress + "] down(): looping back " + msg + ", hdrs:\n" + msg.getHeaders()); + loopback(msg); + } + return null; + } + + if(!multicast && drop_down_unicasts > 0) { + drop_down_unicasts=Math.max(0, drop_down_unicasts -1); + return null; + } + + if(multicast && drop_down_multicasts > 0) { + drop_down_multicasts=Math.max(0, drop_down_multicasts -1); + return null; + } + + if(down > 0) { + r=Math.random(); + if(r < down) { + if(excludeItself && msg.getSrc().equals(localAddress)) { + if(log.isTraceEnabled()) log.trace("excluding itself"); + } + else { + if(log.isTraceEnabled()) + log.trace("dropping message"); + num_down++; + return null; + } + } + } + break; + case Event.VIEW_CHANGE: + View view=(View)evt.getArg(); + Vector
mbrs=view.getMembers(); + ignoredMembers.retainAll(mbrs); // remove all non members + if(discard_dialog != null) + discard_dialog.handleView(mbrs); + break; + } + + return down_prot.down(evt); + } + + private void loopback(Message msg) { + final Message rsp=msg.copy(true); + if(rsp.getSrc() == null) + rsp.setSrc(localAddress); + + // pretty inefficient: creates one thread per message, okay for testing only + Thread thread=new Thread(new Runnable() { + public void run() { + up_prot.up(new Event(Event.MSG, rsp)); + } + }); + thread.start(); + } + + public void resetStats() { + super.resetStats(); + num_down=num_up=0; + } + + public Map dumpStats() { + Map m=new HashMap(2); + m.put("num_dropped_down", new Integer(num_down)); + m.put("num_dropped_up", new Integer(num_up)); + return m; + } + + public static class DiscardHeader extends Header implements Streamable { + + private final Set
dropMessages; + private static final long serialVersionUID=-2149735838082082084L; + + public DiscardHeader() { + this.dropMessages= new HashSet
(); + } + + public DiscardHeader(Set
ignoredAddresses) { + super(); + this.dropMessages= ignoredAddresses; + } + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + int size = in.readShort(); + if (size > 0) { + dropMessages.clear(); + for (int i = 0; i < size; i++) { + dropMessages.add(Util.readAddress(in)); + } + } + } + + public void writeTo(DataOutputStream out) throws IOException { + if (dropMessages != null && !dropMessages.isEmpty()) { + out.writeShort(dropMessages.size()); + for (Address addr: dropMessages) { + Util.writeAddress(addr, out); + } + } else { + out.writeShort(0); + } + + } + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + Set
tmp = (Set
) in.readObject(); + dropMessages.clear(); + dropMessages.addAll(tmp); + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeObject(dropMessages); + } + } + + + private class DiscardDialog extends JFrame implements ActionListener { + private JButton start_discarding_button=new JButton("start discarding"); + private JButton stop_discarding_button=new JButton("stop discarding"); + JPanel checkboxes=new JPanel(); + + + private DiscardDialog() { + } + + void init() { + getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS)); + checkboxes.setLayout(new BoxLayout(checkboxes, BoxLayout.Y_AXIS)); + getContentPane().add(start_discarding_button); + getContentPane().add(stop_discarding_button); + start_discarding_button.addActionListener(this); + stop_discarding_button.addActionListener(this); + getContentPane().add(checkboxes); + pack(); + setVisible(true); + setTitle(localAddress != null? localAddress.toString() : "n/a"); + } + + + public void actionPerformed(ActionEvent e) { + String command=e.getActionCommand(); + if(command.startsWith("start")) { + discard_all=true; + } + else if(command.startsWith("stop")) { + discard_all=false; + Component[] comps=checkboxes.getComponents(); + for(Component c: comps) { + if(c instanceof JCheckBox) { + ((JCheckBox)c).setSelected(false); + } + } + } + } + + void handleView(Collection
mbrs) { + checkboxes.removeAll(); + for(final Address addr: mbrs) { + final MyCheckBox box=new MyCheckBox("discard traffic from " + addr, addr); + box.setAlignmentX(LEFT_ALIGNMENT); + box.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + if(box.isSelected()) { + ignoredMembers.add(addr); + } + else { + ignoredMembers.remove(addr); + } + } + }); + checkboxes.add(box); + } + + for(Component comp: checkboxes.getComponents()) { + MyCheckBox box=(MyCheckBox)comp; + if(ignoredMembers.contains(box.mbr)) + box.setSelected(true); + } + pack(); + } + } + + private static class MyCheckBox extends JCheckBox { + final Address mbr; + + public MyCheckBox(String name, Address member) { + super(name); + this.mbr=member; + } + + public float getAlignmentX() { + return LEFT_ALIGNMENT; + } + + public String toString() { + return super.toString() + " [mbr=" + mbr + "]"; + } + } +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/DISCARD_PAYLOAD.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/DISCARD_PAYLOAD.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/DISCARD_PAYLOAD.java 17 Aug 2012 14:51:05 -0000 1.1 @@ -0,0 +1,82 @@ +package org.jgroups.protocols; + +import org.jgroups.Event; +import org.jgroups.Message; +import org.jgroups.stack.Protocol; + +import java.util.Properties; + +/** + * Discards a message whose sequence number (in the payload, as a Long) matches seqno 2 times, + * before passing it up. Used for unit testing + * of OOB messages + * @author Bela Ban + * @version $Id: DISCARD_PAYLOAD.java,v 1.1 2012/08/17 14:51:05 marcin Exp $ + */ +public class DISCARD_PAYLOAD extends Protocol { + long seqno=3; // drop 3 + long duplicate=4; // duplicate 4 (one time) + int num_discards=0; + + public DISCARD_PAYLOAD() { + } + + public String getName() { + return "DISCARD_PAYLOAD"; + } + + public boolean setProperties(Properties props) { + String str; + + super.setProperties(props); + + str=props.getProperty("seqno"); + if(str != null) { + seqno=Long.parseLong(str); + props.remove("seqno"); + } + + str=props.getProperty("duplicate"); + if(str != null) { + duplicate=Long.parseLong(str); + props.remove("duplicate"); + } + + if(!props.isEmpty()) { + log.error("these properties are not recognized: " + props); + return false; + } + return true; + } + + + public Object up(Event evt) { + if(evt.getType() == Event.MSG) { + Message msg=(Message)evt.getArg(); + if(msg.getLength() > 0) { + try { + Long payload=(Long)msg.getObject(); + if(payload != null) { + long val=payload.longValue(); + + if(val == seqno) { + synchronized(this) { + if(num_discards < 3) { + num_discards++; + return null; + } + } + } + if(val == duplicate) { // inject a duplicate message + super.up(evt); // pass it up, will passed up a second time by the default up_prot.up(evt) + } + } + } + catch(Throwable t) { + ; + } + } + } + return up_prot.up(evt); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/DUMMY_TP.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/DUMMY_TP.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/DUMMY_TP.java 17 Aug 2012 14:51:05 -0000 1.1 @@ -0,0 +1,73 @@ +// $Id: DUMMY_TP.java,v 1.1 2012/08/17 14:51:05 marcin Exp $ + +package org.jgroups.protocols; + + +import org.jgroups.Address; +import org.jgroups.Event; +import org.jgroups.stack.Protocol; + + +/** + * Dummy transport, returns a fake local address and responds to CONNECT. + * Compared to LOOPBACK, this discards everything + * @author Bela Ban + * @version $Id: DUMMY_TP.java,v 1.1 2012/08/17 14:51:05 marcin Exp $ + */ +public class DUMMY_TP extends Protocol { + private Address local_addr=null; + + public DUMMY_TP() { + } + + + public String toString() { + return "Protocol DUMMY_TP (local address: " + local_addr + ')'; + } + + + /*------------------------------ Protocol interface ------------------------------ */ + + public String getName() { + return "DUMMY_TP"; + } + + + + + public void init() throws Exception { + local_addr=new org.jgroups.stack.IpAddress("localhost", 10000); // fake address + } + + public void start() throws Exception { + up_prot.up(new Event(Event.SET_LOCAL_ADDRESS, local_addr)); + } + + + /** + * Caller by the layer above this layer. Usually we just put this Message + * into the send queue and let one or more worker threads handle it. A worker thread + * then removes the Message from the send queue, performs a conversion and adds the + * modified Message to the send queue of the layer below it, by calling Down). + */ + public Object down(Event evt) { + + switch(evt.getType()) { + + case Event.CONNECT: + return null; + + case Event.DISCONNECT: + return null; + } + return down_prot.down(evt); + } + + + + /*--------------------------- End of Protocol interface -------------------------- */ + + + + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/DUPL.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/DUPL.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/DUPL.java 17 Aug 2012 14:51:06 -0000 1.1 @@ -0,0 +1,122 @@ +package org.jgroups.protocols; + +import org.jgroups.stack.Protocol; +import org.jgroups.Event; +import org.jgroups.Message; +import org.jgroups.Address; + +import java.util.Properties; + +/** Duplicates outgoing or incoming messages by copying them + * @author Bela Ban + * @version $Id: DUPL.java,v 1.1 2012/08/17 14:51:06 marcin Exp $ + */ +public class DUPL extends Protocol { + private static enum Direction {UP,DOWN}; + + protected int incoming_copies=1; + + protected int outgoing_copies=1; + + protected boolean copy_unicast_msgs=true; + + protected boolean copy_multicast_msgs=true; + + + public DUPL() { + } + + public DUPL(boolean copy_multicast_msgs, boolean copy_unicast_msgs, int incoming_copies, int outgoing_copies) { + this.copy_multicast_msgs=copy_multicast_msgs; + this.copy_unicast_msgs=copy_unicast_msgs; + this.incoming_copies=incoming_copies; + this.outgoing_copies=outgoing_copies; + } + + public String getName() { + return "DUPL"; + } + + public boolean setProperties(Properties props) { + String str; + + super.setProperties(props); + str=props.getProperty("incoming_copies"); + if(str != null) { + incoming_copies=Integer.parseInt(str); + props.remove("incoming_copies"); + } + + str=props.getProperty("outgoing_copies"); + if(str != null) { + outgoing_copies=Integer.parseInt(str); + props.remove("outgoing_copies"); + } + + str=props.getProperty("copy_unicast_msgs"); + if(str != null) { + copy_unicast_msgs=Boolean.parseBoolean(str); + props.remove("copy_unicast_msgs"); + } + + str=props.getProperty("copy_multicast_msgs"); + if(str != null) { + copy_multicast_msgs=Boolean.parseBoolean(str); + props.remove("copy_multicast_msgs"); + } + + if(!props.isEmpty()) { + log.error("the following properties are not recognized: " + props); + return false; + } + return true; + } + + public Object down(Event evt) { + boolean copy=(copy_multicast_msgs || copy_unicast_msgs) && outgoing_copies > 0; + if(!copy) + return down_prot.down(evt); + + switch(evt.getType()) { + case Event.MSG: + Message msg=(Message)evt.getArg(); + copy(msg, outgoing_copies, Direction.DOWN); + break; + } + + return down_prot.down(evt); + } + + public Object up(Event evt) { + boolean copy=(copy_multicast_msgs || copy_unicast_msgs) && incoming_copies > 0; + if(!copy) + return up_prot.up(evt); + + switch(evt.getType()) { + case Event.MSG: + Message msg=(Message)evt.getArg(); + copy(msg, incoming_copies, Direction.UP); + break; + } + + return up_prot.up(evt); + } + + private void copy(Message msg, int num_copies, Direction direction) { + Address dest=msg.getDest(); + boolean multicast=dest == null || dest.isMulticastAddress(); + if((multicast && copy_multicast_msgs) || (!multicast && copy_unicast_msgs)) { + for(int i=0; i < num_copies; i++) { + Message copy=msg.copy(true); + switch(direction) { + case UP: + up_prot.up(new Event(Event.MSG, copy)); + break; + case DOWN: + down_prot.down(new Event(Event.MSG, copy)); + break; + } + } + } + } +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/Discovery.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/Discovery.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/Discovery.java 17 Aug 2012 14:51:06 -0000 1.1 @@ -0,0 +1,511 @@ +package org.jgroups.protocols; + +import org.jgroups.*; +import org.jgroups.protocols.pbcast.JoinRsp; +import org.jgroups.stack.Protocol; +import org.jgroups.util.Promise; +import org.jgroups.util.TimeScheduler; +import org.jgroups.util.Util; + +import java.io.InterruptedIOException; +import java.util.*; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + + +/** + * The Discovery protocol layer retrieves the initial membership (used by the GMS when started + * by sending event FIND_INITIAL_MBRS down the stack). We do this by specific subclasses, e.g. by mcasting PING + * requests to an IP MCAST address or, if gossiping is enabled, by contacting the GossipRouter. + * The responses should allow us to determine the coordinator whom we have to + * contact, e.g. in case we want to join the group. When we are a server (after having + * received the BECOME_SERVER event), we'll respond to PING requests with a PING + * response.

The FIND_INITIAL_MBRS event will eventually be answered with a + * FIND_INITIAL_MBRS_OK event up the stack. + * The following properties are available + *

    + *
  • timeout - the timeout (ms) to wait for the initial members, default is 3000=3 secs + *
  • num_initial_members - the minimum number of initial members for a FIND_INITAL_MBRS, default is 2 + *
  • num_ping_requests - the number of GET_MBRS_REQ messages to be sent (min=1), distributed over timeout ms + *
+ * @author Bela Ban + * @version $Id: Discovery.java,v 1.1 2012/08/17 14:51:06 marcin Exp $ + */ +public abstract class Discovery extends Protocol { + final Vector
members=new Vector
(11); + Address local_addr=null; + String group_addr=null; + long timeout=3000; + int num_initial_members=2; + boolean is_server=false; + TimeScheduler timer=null; + + /** Number of GET_MBRS_REQ messages to be sent (min=1), distributed over timeout ms */ + int num_ping_requests=2; + int num_discovery_requests=0; + + /** Minimum number of server responses (PingRsp.isServer()=true). If this value is greater than 0, we'll ignore num_initial_members */ + int num_initial_srv_members=0; + + /** Return from the discovery phase as soon as we have 1 coordinator response */ + boolean break_on_coord_rsp=true; + + + private final Set ping_responses=new HashSet(); + private final PingSenderTask sender=new PingSenderTask(); + + + + public void init() throws Exception { + timer=getTransport().getTimer(); + if(timer == null) + throw new Exception("timer cannot be retrieved from protocol stack"); + } + + + /** Called after local_addr was set */ + public void localAddressSet(Address addr) { + } + + public abstract void sendGetMembersRequest(Promise promise) throws Exception; + + + public void handleDisconnect() { + } + + public void handleConnect() { + } + + public long getTimeout() { + return timeout; + } + + public void setTimeout(long timeout) { + this.timeout=timeout; + } + + public int getNumInitialMembers() { + return num_initial_members; + } + + public void setNumInitialMembers(int num_initial_members) { + this.num_initial_members=num_initial_members; + } + + public int getNumPingRequests() { + return num_ping_requests; + } + + public void setNumPingRequests(int num_ping_requests) { + this.num_ping_requests=num_ping_requests; + } + + public int getNumberOfDiscoveryRequestsSent() { + return num_discovery_requests; + } + + + public Vector providedUpServices() { + Vector ret=new Vector(1); + ret.addElement(new Integer(Event.FIND_INITIAL_MBRS)); + return ret; + } + + /** + * sets the properties of the PING protocol. + * The following properties are available + * property: timeout - the timeout (ms) to wait for the initial members, default is 3000=3 secs + * property: num_initial_members - the minimum number of initial members for a FIND_INITAL_MBRS, default is 2 + * @param props - a property set + * @return returns true if all properties were parsed properly + * returns false if there are unrecnogized properties in the property set + */ + public boolean setProperties(Properties props) { + String str; + + super.setProperties(props); + str=props.getProperty("timeout"); // max time to wait for initial members + if(str != null) { + timeout=Long.parseLong(str); + if(timeout <= 0) { + if(log.isErrorEnabled()) log.error("timeout must be > 0"); + return false; + } + props.remove("timeout"); + } + + str=props.getProperty("num_initial_members"); // wait for at most n members + if(str != null) { + num_initial_members=Integer.parseInt(str); + props.remove("num_initial_members"); + } + + str=props.getProperty("num_initial_srv_members"); // wait for at most n server members + if(str != null) { + num_initial_srv_members=Integer.parseInt(str); + props.remove("num_initial_srv_members"); + } + + str=props.getProperty("break_on_coord_rsp"); // terminate discovery phase when we receive 1 response from a coord + if(str != null) { + break_on_coord_rsp=Boolean.parseBoolean(str); + props.remove("break_on_coord_rsp"); + } + + str=props.getProperty("num_ping_requests"); // number of GET_MBRS_REQ messages + if(str != null) { + num_ping_requests=Integer.parseInt(str); + props.remove("num_ping_requests"); + if(num_ping_requests < 1) + num_ping_requests=1; + } + + if(!props.isEmpty()) { + StringBuilder sb=new StringBuilder(); + for(Enumeration e=props.propertyNames(); e.hasMoreElements();) { + sb.append(e.nextElement().toString()); + if(e.hasMoreElements()) { + sb.append(", "); + } + } + if(log.isErrorEnabled()) log.error("The following properties are not recognized: " + sb); + return false; + } + return true; + } + + public void resetStats() { + super.resetStats(); + num_discovery_requests=0; + } + + public void start() throws Exception { + super.start(); + } + + public void stop() { + is_server=false; + } + + /** + * Finds the initial membership: sends a GET_MBRS_REQ to all members, waits 'timeout' ms or + * until 'num_initial_members' have been retrieved + * @return List + */ + public List findInitialMembers(Promise promise) { + num_discovery_requests++; + + final Responses rsps=new Responses(num_initial_members, num_initial_srv_members, break_on_coord_rsp, promise); + synchronized(ping_responses) { + ping_responses.add(rsps); + } + + sender.start(promise); + try { + return rsps.get(timeout); + } + catch(Exception e) { + return new LinkedList(); + } + finally { + sender.stop(); + synchronized(ping_responses) { + ping_responses.remove(rsps); + } + } + } + + + public String findInitialMembersAsString() { + List results=findInitialMembers(null); + if(results == null || results.isEmpty()) return ""; + StringBuilder sb=new StringBuilder(); + for(PingRsp rsp: results) { + sb.append(rsp).append("\n"); + } + return sb.toString(); + } + + + /** + * An event was received from the layer below. Usually the current layer will want to examine + * the event type and - depending on its type - perform some computation + * (e.g. removing headers from a MSG event type, or updating the internal membership list + * when receiving a VIEW_CHANGE event). + * Finally the event is either a) discarded, or b) an event is sent down + * the stack using PassDown or c) the event (or another event) is sent up + * the stack using PassUp. + *

+ * For the PING protocol, the Up operation does the following things. + * 1. If the event is a Event.MSG then PING will inspect the message header. + * If the header is null, PING simply passes up the event + * If the header is PingHeader.GET_MBRS_REQ then the PING protocol + * will PassDown a PingRequest message + * If the header is PingHeader.GET_MBRS_RSP we will add the message to the initial members + * vector and wake up any waiting threads. + * 2. If the event is Event.SET_LOCAL_ADDR we will simple set the local address of this protocol + * 3. For all other messages we simple pass it up to the protocol above + * + * @param evt - the event that has been sent from the layer below + */ + + public Object up(Event evt) { + Message msg, rsp_msg; + PingHeader rsp_hdr; + PingRsp rsp; + Address coord; + + switch(evt.getType()) { + + case Event.MSG: + msg=(Message)evt.getArg(); + PingHeader hdr=(PingHeader)msg.getHeader(getName()); + if(hdr == null) { + return up_prot.up(evt); + } + + switch(hdr.type) { + + case PingHeader.GET_MBRS_REQ: // return Rsp(local_addr, coord) + if(local_addr != null && msg.getSrc() != null && local_addr.equals(msg.getSrc())) { + return null; + } + synchronized(members) { + coord=!members.isEmpty()? members.firstElement() : local_addr; + } + + PingRsp ping_rsp=new PingRsp(local_addr, coord, is_server); + rsp_msg=new Message(msg.getSrc(), null, null); + rsp_msg.setFlag(Message.OOB); + rsp_hdr=new PingHeader(PingHeader.GET_MBRS_RSP, ping_rsp); + rsp_msg.putHeader(getName(), rsp_hdr); + if(log.isTraceEnabled()) + log.trace("received GET_MBRS_REQ from " + msg.getSrc() + ", sending response " + rsp_hdr); + down_prot.down(new Event(Event.MSG, rsp_msg)); + return null; + + case PingHeader.GET_MBRS_RSP: // add response to vector and notify waiting thread + rsp=hdr.arg; + + if(log.isTraceEnabled()) + log.trace("received GET_MBRS_RSP, rsp=" + rsp); + // myfuture.addResponse(rsp); + + synchronized(ping_responses) { + for(Responses rsps: ping_responses) + rsps.addResponse(rsp); + } + return null; + + default: + if(log.isWarnEnabled()) log.warn("got PING header with unknown type (" + hdr.type + ')'); + return null; + } + + case Event.SET_LOCAL_ADDRESS: + up_prot.up(evt); + local_addr=(Address)evt.getArg(); + localAddressSet(local_addr); + break; + + default: + up_prot.up(evt); // Pass up to the layer above us + break; + } + + return null; + } + + + /** + * An event is to be sent down the stack. The layer may want to examine its type and perform + * some action on it, depending on the event's type. If the event is a message MSG, then + * the layer may need to add a header to it (or do nothing at all) before sending it down + * the stack using PassDown. In case of a GET_ADDRESS event (which tries to + * retrieve the stack's address from one of the bottom layers), the layer may need to send + * a new response event back up the stack using up_prot.up(). + * The PING protocol is interested in several different down events, + * Event.FIND_INITIAL_MBRS - sent by the GMS layer and expecting a GET_MBRS_OK + * Event.TMP_VIEW and Event.VIEW_CHANGE - a view change event + * Event.BECOME_SERVER - called after client has joined and is fully working group member + * Event.CONNECT, Event.DISCONNECT. + */ + public Object down(Event evt) { + + switch(evt.getType()) { + + case Event.FIND_INITIAL_MBRS: // sent by GMS layer, pass up a GET_MBRS_OK event + // sends the GET_MBRS_REQ to all members, waits 'timeout' ms or until 'num_initial_members' have been retrieved + long start=System.currentTimeMillis(); + List rsps=findInitialMembers((Promise)evt.getArg()); + long diff=System.currentTimeMillis() - start; + if(log.isTraceEnabled()) + log.trace("discovery took "+ diff + " ms: responses: " + Util.printPingRsps(rsps)); + return rsps; + + case Event.TMP_VIEW: + case Event.VIEW_CHANGE: + Vector

tmp; + if((tmp=((View)evt.getArg()).getMembers()) != null) { + synchronized(members) { + members.clear(); + members.addAll(tmp); + } + } + return down_prot.down(evt); + + case Event.BECOME_SERVER: // called after client has joined and is fully working group member + down_prot.down(evt); + is_server=true; + return null; + + case Event.CONNECT: + case Event.CONNECT_WITH_STATE_TRANSFER: + group_addr=(String)evt.getArg(); + Object ret=down_prot.down(evt); + handleConnect(); + return ret; + + case Event.DISCONNECT: + handleDisconnect(); + return down_prot.down(evt); + + default: + return down_prot.down(evt); // Pass on to the layer below us + } + } + + + + /* -------------------------- Private methods ---------------------------- */ + + + @SuppressWarnings("unchecked") + protected final View makeView(Vector mbrs) { + Address coord; + long id; + ViewId view_id=new ViewId(local_addr); + + coord=view_id.getCoordAddress(); + id=view_id.getId(); + return new View(coord, id, mbrs); + } + + + + class PingSenderTask { + private Future senderFuture; + + public PingSenderTask() {} + + public synchronized void start(final Promise promise) { + long delay = (long)(timeout / (double)num_ping_requests); + if(senderFuture == null || senderFuture.isDone()) { + senderFuture=timer.scheduleWithFixedDelay(new Runnable() { + public void run() { + try { + sendGetMembersRequest(promise); + } + catch(InterruptedIOException ie) { + if(log.isWarnEnabled()){ + log.warn("Discovery request interrupted"); + } + Thread.currentThread().interrupt(); + } + catch(Exception ex) { + if(log.isErrorEnabled()) + log.error("failed sending discovery request", ex); + } + } + }, 0, delay, TimeUnit.MILLISECONDS); + } + } + + public synchronized void stop() { + if(senderFuture != null) { + senderFuture.cancel(true); + senderFuture=null; + } + } + } + + + private static class Responses { + final Promise promise; + final List ping_rsps=new LinkedList(); + final int num_expected_rsps; + final int num_expected_srv_rsps; + final boolean break_on_coord_rsp; + + public Responses(int num_expected_rsps, int num_expected_srv_rsps, boolean break_on_coord_rsp, Promise promise) { + this.num_expected_rsps=num_expected_rsps; + this.num_expected_srv_rsps=num_expected_srv_rsps; + this.break_on_coord_rsp=break_on_coord_rsp; + this.promise=promise != null? promise : new Promise(); + } + + public void addResponse(PingRsp rsp) { + if(rsp == null) + return; + promise.getLock().lock(); + try { + if(!ping_rsps.contains(rsp)) { + ping_rsps.add(rsp); + promise.getCond().signalAll(); + } + } + finally { + promise.getLock().unlock(); + } + } + + public List get(long timeout) throws InterruptedException { + long start_time=System.currentTimeMillis(), time_to_wait=timeout; + + promise.getLock().lock(); + try { + while(time_to_wait > 0 && !promise.hasResult()) { + // if num_expected_srv_rsps > 0, then it overrides num_expected_rsps + if(num_expected_srv_rsps > 0) { + int received_srv_rsps=getNumServerResponses(ping_rsps); + if(received_srv_rsps >= num_expected_srv_rsps) + return new LinkedList(ping_rsps); + } + else if(ping_rsps.size() >= num_expected_rsps) { + return new LinkedList(ping_rsps); + } + + if(break_on_coord_rsp && containsCoordinatorResponse(ping_rsps)) + return new LinkedList(ping_rsps); + + promise.getCond().await(time_to_wait, TimeUnit.MILLISECONDS); + time_to_wait=timeout - (System.currentTimeMillis() - start_time); + } + return new LinkedList(ping_rsps); + } + finally { + promise.getLock().unlock(); + } + } + + private static int getNumServerResponses(List rsps) { + int cnt=0; + for(PingRsp rsp: rsps) { + if(rsp.isServer()) + cnt++; + } + return cnt; + } + + private static boolean containsCoordinatorResponse(List rsps) { + if(rsps == null || rsps.isEmpty()) + return false; + for(PingRsp rsp: rsps) { + if(rsp.isCoord()) + return true; + } + return false; + } + + } +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/ENCRYPT.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/ENCRYPT.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/ENCRYPT.java 17 Aug 2012 14:51:08 -0000 1.1 @@ -0,0 +1,1444 @@ +// $Id: ENCRYPT.java,v 1.1 2012/08/17 14:51:08 marcin Exp $ + +package org.jgroups.protocols; + +import org.jgroups.*; +import org.jgroups.stack.Protocol; +import org.jgroups.util.QueueClosedException; +import org.jgroups.util.Streamable; +import org.jgroups.util.Util; + + +import javax.crypto.*; +import javax.crypto.spec.SecretKeySpec; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.*; +import java.security.cert.CertificateException; +import java.security.spec.X509EncodedKeySpec; +import java.util.Map; +import java.util.Properties; +import java.util.WeakHashMap; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; + + +/** + * ENCRYPT layer. Encrypt and decrypt the group communication in JGroups + * + * The file can be used in two ways: + *
    + *
  • Option 1. Configured with a secretKey in a keystore so it can be used at any + * layer in JGroups without the need for a coordinator, or if you want protection against passive + * monitoring but do not want the key exchange overhead and complexity. In this mode all nodes must be distributed with + * the same keystore file. + *
  • Option 2. Configured with algorithms and key sizes. The Encrypt Layer in this mode sould be used between the + * FRAG and PBCast layers in the stack. The coordinator then chooses the + * secretkey which it distributes amongst all the peers. In this form no keystore exists as the keys are + * distributed using a public/private key exchange. View changes that identify a new controller will result in a new session key + * being generated and then distributed to all peers. This overhead can be substantial in a an application with + * a reasonable peer churn. + *
+ *

+ *

+ * Each message is identified as encrypted with a specific encryption header which identifies + * the type of encrypt header and an MD5 digest that identifies the version of the key being used + * to encrypt/decrypt the messages. + *

+ *

+ *

Option 1


+ * This is the simplest option and can be used by simply inserting the Encryption layer + * at any point in the JGroup stack - it will encrypt all Events of a type MSG that + * have a non-null message buffer. The format of the entry in this form is:
+ * <ENCRYPT key_store_name="defaultStore.keystore" store_password="changeit" alias="myKey"/>
+ * An example bare-bones.xml file showing the keystore version can be found in the conf + * ina file called EncryptKeyStore.xml - along with a defaultStore.keystore file.
+ * In order to use the Encrypt layer in this manner it is necessary to have the secretKey already generated + * in a keystore file. The directory containing the keystore file must be on the application's classpath. + * You cannot create a SecretKey keystore file using the keytool application shipped with the JDK. + * A java file called KeyStoreGenerator is included in the demo + * package that can be used from the command line (or IDE) to generate a suitable keystore. + *

+ *

+ *

Option 2


+ * This option is suited to an application that does not ship with a known key but instead it is generated + * and distributed by the controller. The secret key is first generated by the Controller (in JGroup terms). When a view change + * occurs a peer will request the secret key by sending a key request with its own public key. The controller + * encrypts the secret key with this key and sends it back to the peer who then decrypts it and installs the + * key as its own secret key. + *
All encryption and decryption of Messages is done using this key. When a peer receives + * a view change that shows a different keyserver it will repeat this process - the view change event + * also trigger the encrypt layer to queue up and down messages until the new key is installed. + * The previous keys are retained so that messages sent before the view change that are queued can be decrypted + * if the key is different. + *
+ * An example EncryptNoKeyStore.xml is included in the conf file as a guide. + *

+ *
Note: the current version does not support the concept of perfect forward encryption (PFE) + * which means that if a peer leaves the group the keys are re-generated preventing the departed peer from + * decrypting future messages if it chooses to listen in on the group. This is not included as it really requires + * a suitable authentication scheme as well to make this feature useful as there is nothing to stop the peer rejoining and receiving the new + * key. A future release will address this issue. + * + * @author Steve Woodcock + * @author Bela Ban + */ +public class ENCRYPT extends Protocol { + Observer observer; + + interface Observer { + void up(Event evt); + void passUp(Event evt); + void down(Event evt); + void passDown(Event evt); + } + + + static final String DEFAULT_SYM_ALGO = "Blowfish"; + // address info + Address local_addr = null; + // keyserver address + Address keyServerAddr = null; + + //used to see whether we are the key server + boolean keyServer = false; + + // encryption properties in no supplied key mode + String asymProvider = null; + static final String symProvider = null; + String asymAlgorithm = "RSA"; + String symAlgorithm = DEFAULT_SYM_ALGO; + int asymInit = 512; // initial public/private key length + int symInit = 56; // initial shared key length + + // properties for functioning in supplied key mode + private boolean suppliedKey = false; + private String keyStoreName; + private String storePassword ="changeit"; //JDK default + private String keyPassword="changeit"; //JDK default + private String alias="mykey"; // JDK default + + + // public/private Key + KeyPair Kpair; // to store own's public/private Key + +// for client to store server's public Key + PublicKey serverPubKey = null; + + // needed because we do simultaneous encode/decode with these ciphers - which + // would be a threading issue + Cipher symEncodingCipher; + Cipher symDecodingCipher; + + // version filed for secret key + private String symVersion = null; + // dhared secret key to encrypt/decrypt messages + SecretKey secretKey = null; + + // map to hold previous keys so we can decrypt some earlier messages if we need to + final Map keyMap = new WeakHashMap(); + + // queues to buffer data while we are swapping shared key + // or obtsining key for first time + + private boolean queue_up = true; + + private boolean queue_down = false; + + // queue to hold upcoming messages while key negotiation is happening + private BlockingQueue upMessageQueue = new LinkedBlockingQueue(); + +// queue to hold downcoming messages while key negotiation is happening + private BlockingQueue downMessageQueue = new LinkedBlockingQueue(); + // decrypting cypher for secret key requests + private Cipher asymCipher; + + /** determines whether to encrypt the entire message, or just the buffer */ + private boolean encrypt_entire_message=false; + + + public ENCRYPT() + { + } + + + public String getName() + { + return "ENCRYPT"; + } + + + public void setObserver(Observer o) { + observer=o; + } + + /* + * GetAlgorithm: Get the algorithm name from "algorithm/mode/padding" + * taken m original ENCRYPT file + */ + private static String getAlgorithm(String s) + { + int index = s.indexOf("/"); + if (index == -1) + return s; + + return s.substring(0, index); + } + + + public boolean setProperties(Properties props) + { + String str; + + super.setProperties(props); + // asymmetric key length + str = props.getProperty("asym_init"); + if (str != null) + { + asymInit = Integer.parseInt(str); + props.remove("asym_init"); + + if (log.isInfoEnabled()) + log.info("Asym algo bits used is " + asymInit); + } + + // symmetric key length + str = props.getProperty("sym_init"); + if (str != null) + { + symInit = Integer.parseInt(str); + props.remove("sym_init"); + + if (log.isInfoEnabled()) + log.info("Sym algo bits used is " + symInit); + } + + // asymmetric algorithm name + str = props.getProperty("asym_algorithm"); + if (str != null) + { + asymAlgorithm = str; + props.remove("asym_algorithm"); + + if (log.isInfoEnabled()) + log.info("Asym algo used is " + asymAlgorithm); + } + + // symmetric algorithm name + str = props.getProperty("sym_algorithm"); + if (str != null) + { + symAlgorithm = str; + props.remove("sym_algorithm"); + + if (log.isInfoEnabled()) + log.info("Sym algo used is " + symAlgorithm); + } + + // symmetric algorithm name + str = props.getProperty("asym_provider"); + if (str != null) + { + asymProvider = str; + props.remove("asym_provider"); + + if (log.isInfoEnabled()) + log.info("asymProvider used is " + asymProvider); + } + + //symmetric algorithm name + str = props.getProperty("key_store_name"); + if (str != null) + { + keyStoreName = str; + props.remove("key_store_name"); + + if (log.isInfoEnabled()) + log.info("key_store_name used is " + keyStoreName); + } + + // symmetric algorithm name + str = props.getProperty("store_password"); + if (str != null) + { + storePassword = str; + props.remove("store_password"); + + if (log.isInfoEnabled()) + log.info("store_password used is not null"); + } + + // symmetric algorithm name + str = props.getProperty("key_password"); + if (str != null) + { + keyPassword = str; + props.remove("key_password"); + + if (log.isInfoEnabled()) + log.info("key_password used is not null"); + } else if (storePassword != null) + { + keyPassword = storePassword; + + if (log.isInfoEnabled()) + log.info("key_password used is same as store_password"); + } + + // symmetric algorithm name + str = props.getProperty("alias"); + if (str != null) + { + alias = str; + props.remove("alias"); + + if (log.isInfoEnabled()) + log.info("alias used is " + alias); + } + + str=props.getProperty("encrypt_entire_message"); + if(str != null) + { + this.encrypt_entire_message=Boolean.valueOf(str).booleanValue(); + props.remove("encrypt_entire_message"); + } + + if (!props.isEmpty()) + { + + if (log.isErrorEnabled()) + log.error("these properties are not recognized:" + props); + return false; + } + + return true; + } + + + public void init() throws Exception + { + if (keyStoreName == null) + { + initSymKey(); + initKeyPair(); + } else + { + initConfiguredKey(); + } + initSymCiphers(symAlgorithm, getSecretKey()); + } + + + /** + * Initialisation if a supplied key is defined in the properties. This + * supplied key must be in a keystore which can be generated using the + * keystoreGenerator file in demos. The keystore must be on the classpath + * to find it. + * + * @throws KeyStoreException + * @throws Exception + * @throws IOException + * @throws NoSuchAlgorithmException + * @throws CertificateException + * @throws UnrecoverableKeyException + */ + private void initConfiguredKey() throws Exception + { + InputStream inputStream = null; + // must not use default keystore type - as does not support secret keys + KeyStore store = KeyStore.getInstance("JCEKS"); + + SecretKey tempKey = null; + try + { + // load in keystore using this thread's classloader + inputStream = Thread.currentThread().getContextClassLoader() + .getResourceAsStream(keyStoreName); + // we can't find a keystore here - + if (inputStream == null) + { + throw new Exception("Unable to load keystore " + keyStoreName + + " ensure file is on classpath"); + } + // we have located a file lets load the keystore + try{ + store.load(inputStream, storePassword.toCharArray()); + // loaded keystore - get the key + tempKey = (SecretKey) store + .getKey(alias, keyPassword.toCharArray()); + } catch (IOException e){ + throw new Exception("Unable to load keystore "+ keyStoreName + ": " + e); + }catch (NoSuchAlgorithmException e){ + throw new Exception("No Such algorithm "+ keyStoreName + ": " + e); + }catch(CertificateException e){ + throw new Exception("Certificate exception "+ keyStoreName + ": " + e); + } + + if (tempKey == null) + { + throw new Exception("Unable to retrieve key '" + alias + + "' from keystore " + keyStoreName); + } + //set the key here + setSecretKey(tempKey); + + if (symAlgorithm.equals(DEFAULT_SYM_ALGO)) { + symAlgorithm = tempKey.getAlgorithm(); + } + + // set the fact we are using a supplied key + + suppliedKey = true; + queue_down =false; + queue_up =false; + } finally + { + Util.close(inputStream); + } + + } + + + /** + * Used to initialise the symmetric key if none is supplied in a keystore. + * @throws Exception + */ + public void initSymKey() throws Exception + { + KeyGenerator keyGen = null; + // see if we have a provider specified + if (symProvider != null && symProvider.trim().length() > 0) + { + keyGen = KeyGenerator.getInstance(getAlgorithm(symAlgorithm), + symProvider); + } else + { + keyGen = KeyGenerator.getInstance(getAlgorithm(symAlgorithm)); + } + // generate the key using the defined init properties + keyGen.init(symInit); + secretKey = keyGen.generateKey(); + + setSecretKey(secretKey); + + if (log.isInfoEnabled()) + log.info(" Symmetric key generated "); + } + + + /** + * Initialises the Ciphers for both encryption and decryption using the + * generated or supplied secret key. + * + * @param algorithm + * @param secret + * @throws Exception + */ + private void initSymCiphers(String algorithm, SecretKey secret) throws Exception + { + + if (log.isInfoEnabled()) + log.info(" Initializing symmetric ciphers"); + + symEncodingCipher = Cipher.getInstance(algorithm); + symDecodingCipher = Cipher.getInstance(algorithm); + symEncodingCipher.init(Cipher.ENCRYPT_MODE, secret); + symDecodingCipher.init(Cipher.DECRYPT_MODE, secret); + + //set the version + MessageDigest digest = MessageDigest.getInstance("MD5"); + digest.reset(); + digest.update(secret.getEncoded()); + + symVersion = new String(digest.digest(), "UTF-8"); + if (log.isInfoEnabled()) { + // log.info(" Initialized symmetric ciphers with secret key (" + symVersion.length() + " bytes) " +symVersion); + StringBuilder sb=new StringBuilder(" Initialized symmetric ciphers with secret key (" + symVersion.length() + " bytes) "); + char[] arr=symVersion.toCharArray(); + for(int i=0; i < arr.length; i++) { + char c=arr[i]; + sb.append((int)c); + } + log.info(sb.toString()); + } + } + + + /** + * Generates the public/private key pair from the init params + * @throws Exception + */ + public void initKeyPair() throws Exception + { + // generate keys according to the specified algorithms + // generate publicKey and Private Key + KeyPairGenerator KpairGen = null; + if (asymProvider != null && asymProvider.trim().length() > 0) + { + KpairGen = KeyPairGenerator.getInstance( + getAlgorithm(asymAlgorithm), asymProvider); + } else + { + KpairGen = KeyPairGenerator + .getInstance(getAlgorithm(asymAlgorithm)); + + } + KpairGen.initialize(asymInit, new SecureRandom()); + Kpair = KpairGen.generateKeyPair(); + + // set up the Cipher to decrypt secret key responses encrypted with our key + + asymCipher = Cipher.getInstance(asymAlgorithm); + asymCipher.init(Cipher.DECRYPT_MODE,Kpair.getPrivate()); + + if (log.isInfoEnabled()) + log.info(" asym algo initialized"); + } + + + /** Just remove if you don't need to reset any state */ + public void reset() + { + } + + + /* (non-Javadoc) + * @see org.jgroups.stack.Protocol#up(org.jgroups.Event) + */ + public Object up(Event evt) + { + switch (evt.getType()) { + + // we need to know what our address is + case Event.SET_LOCAL_ADDRESS : + local_addr = (Address) evt.getArg(); + if (log.isDebugEnabled()) + log.debug("set local address to " + local_addr); + break; + case Event.VIEW_CHANGE: + View view=(View)evt.getArg(); + if (log.isInfoEnabled()) + log.info("handling view-change up: " + view); + if (!suppliedKey){ + handleViewChange(view, false); + } + break; + case Event.TMP_VIEW: + view=(View)evt.getArg(); + if (log.isInfoEnabled()) + log.info("handling tmp-view up: " + view); + if (!suppliedKey){ + // if a tmp_view then we are trying to become coordinator so + // make us keyserver + handleViewChange(view, true); + } + break; + // we try and decrypt all messages + case Event.MSG : + try + { + handleUpMessage(evt); + } catch (Exception e) + { + log.warn("exception occurred decrypting message", e); + } + return null; + default : + break; + } + return passItUp(evt); + } + + public Object passItUp(Event evt) { + if(observer != null) + observer.passUp(evt); + return up_prot != null? up_prot.up(evt) : null; + } + + + private synchronized void handleViewChange(View view, boolean makeServer){ + + // if view is a bit broken set me as keyserver + if (view.getMembers() == null || view.getMembers().get(0) == null){ + becomeKeyServer(local_addr); + return; + } + // otherwise get keyserver from view controller + Address tmpKeyServer = view.getMembers().get(0); + + //I am new keyserver - either first member of group or old key server is no more and + // I have been voted new controller + if (makeServer + || (tmpKeyServer.equals(local_addr) && (keyServerAddr == null || (!tmpKeyServer.equals(keyServerAddr))))) { + becomeKeyServer(tmpKeyServer); + // a new keyserver has been set and it is not me + }else if (keyServerAddr == null || (! tmpKeyServer.equals(keyServerAddr))){ + handleNewKeyServer(tmpKeyServer); + } else{ + if (log.isDebugEnabled()) + log.debug("Membership has changed but I do not care"); + } + } + + /** + * Handles becoming server - resetting queue settings + * and setting keyserver address to be local address. + * + * @param tmpKeyServer + */ + private void becomeKeyServer(Address tmpKeyServer){ + keyServerAddr = tmpKeyServer; + keyServer =true; + if (log.isInfoEnabled()) + log.info("I have become key server " + keyServerAddr); + queue_down = false; + queue_up = false; + } + + /** + * Sets up the peer for a new keyserver - this is + * setting queueing to buffer messages until we have a new + * secret key from the key server and sending a key request + * to the new keyserver. + * + * @param newKeyServer + */ + private void handleNewKeyServer(Address newKeyServer){ + // start queueing until we have new key + // to make sure we are not sending with old key + queue_up =true; + queue_down = true; + // set new keyserver address + keyServerAddr = newKeyServer; + keyServer =false; + if (log.isInfoEnabled()) + log.info("Sending key request"); + + // create a key request message + sendKeyRequest(); + } + + /** + * @param evt + */ + private void handleUpMessage(Event evt) throws Exception + { + Message msg = (Message) evt.getArg(); + + if (msg == null) + { + if(log.isTraceEnabled()) + log.trace("null message - passing straight up"); + passItUp(evt); + return; + } + + if(msg.getLength() == 0) { + passItUp(evt); + return; + } + + EncryptHeader hdr = (EncryptHeader)msg.getHeader(EncryptHeader.KEY); + + // try and get the encryption header + if (hdr == null) + { + if (log.isTraceEnabled()) + log.trace("dropping message as ENCRYPT header is null or has not been recognized, msg will not be passed up, " + + "headers are " + msg.printHeaders()); + return; + } + + if(log.isTraceEnabled()) + log.trace("header received " + hdr); + + + // if a normal message try and decrypt it + if (hdr.getType() == EncryptHeader.ENCRYPT) + { + // if msg buffer is empty, and we didn't encrypt the entire message, just pass up + if (!hdr.encrypt_entire_msg && ((Message)evt.getArg()).getLength() == 0) { + if(log.isTraceEnabled()) + log.trace("passing up message as it has an empty buffer "); + passItUp(evt); + return; + } + + // if queueing then pass into queue to be dealt with later + if (queue_up){ + if(log.isTraceEnabled()) + log.trace("queueing up message as no session key established: " + evt.getArg()); + upMessageQueue.put(evt); + } else{ + // make sure we pass up any queued messages first + // could be more optimised but this can wait + // we only need this if not using supplied key + if (!suppliedKey) { + drainUpQueue(); + } + // try and decrypt the message - we need to copy msg as we modify its + // buffer (http://jira.jboss.com/jira/browse/JGRP-538) + Message tmpMsg=decryptMessage(symDecodingCipher, msg.copy()); + if (tmpMsg != null){ + if(log.isTraceEnabled()) + log.trace("decrypted message " + tmpMsg); + passItUp(new Event(Event.MSG, tmpMsg)); + } else { + log.warn("Unrecognised cipher discarding message"); + } + } + } else + { + // check if we had some sort of encrypt control + // header if using supplied key we should not + // process it + if (suppliedKey) + { + if (log.isWarnEnabled()) + { + log.warn("We received an encrypt header of " + hdr.getType() + " while in configured mode"); + } + } else{ + // see what sort of encrypt control message we + // have received + switch (hdr.getType()){ + // if a key request + case EncryptHeader.KEY_REQUEST: + if (log.isInfoEnabled()) { + log.info("received a key request from peer"); + } + + //if a key request send response key back + try { + // extract peer's public key + PublicKey tmpKey = generatePubKey(msg.getBuffer()); + // send back the secret key we have + sendSecretKey(getSecretKey(), tmpKey, msg.getSrc()); + } catch (Exception e){ + log.warn("unable to reconstitute peer's public key"); + } + break; + case EncryptHeader.SECRETKEY: + if (log.isInfoEnabled()) { + log.info("received a secretkey response from keyserver"); + } + + try { + SecretKey tmp = decodeKey(msg.getBuffer()); + if (tmp == null) { + // unable to understand response + // lets try again + sendKeyRequest(); + }else{ + // otherwise lets set the reurned key + // as the shared key + setKeys(tmp, hdr.getVersion()); + if (log.isInfoEnabled()) { + log.info("Decoded secretkey response"); + } + } + } catch (Exception e){ + log.warn("unable to process received public key"); + } + break; + default: + log.warn("Received ignored encrypt header of "+hdr.getType()); + break; + } + } + } + } + + + /** + * used to drain the up queue - synchronized so we + * can call it safely despite access from potentially two threads at once + * @throws QueueClosedException + * @throws Exception + */ + private void drainUpQueue() throws Exception + { + //we do not synchronize here as we only have one up thread so we should never get an issue + //synchronized(upLock){ + Event tmp =null; + while ((tmp = (Event)upMessageQueue.poll(0L, TimeUnit.MILLISECONDS)) != null){ + Message msg = decryptMessage(symDecodingCipher, ((Message)tmp.getArg()).copy()); + + if (msg != null){ + if(log.isTraceEnabled()){ + log.trace("passing up message from drain " + msg); + } + passItUp(new Event(Event.MSG, msg)); + } else{ + log.warn("discarding message in queue up drain as cannot decode it"); + } + } + } + + + + /** + * Sets the keys for the app. and drains the queues - the drains could + * be called att he same time as the up/down messages calling in to + * the class so we may have an extra call to the drain methods but this slight expense + * is better than the alternative of waiting until the next message to + * trigger the drains which may never happen. + * + * @param key + * @param version + * @throws Exception + */ + private void setKeys(SecretKey key, String version) throws Exception{ + + // put the previous key into the map + // if the keys are already there then they will overwrite + keyMap.put(getSymVersion(), getSymDecodingCipher()); + + setSecretKey(key); + initSymCiphers(key.getAlgorithm(),key ); + setSymVersion(version); + + // drain the up queue + log.info("setting queue up to false in setKeys"); + queue_up =false; + drainUpQueue(); + + queue_down =false; + drainDownQueue(); + } + + + /** + * Does the actual work for decrypting - if version does not match current cipher + * then tries to use previous cipher + * @param cipher + * @param msg + * @return + * @throws Exception + */ + private Message decryptMessage(Cipher cipher, Message msg) throws Exception + { + EncryptHeader hdr = (EncryptHeader)msg.getHeader(EncryptHeader.KEY); + if (!hdr.getVersion().equals(getSymVersion())){ + log.warn("attempting to use stored cipher as message does not uses current encryption version "); + cipher = (Cipher)keyMap.get(hdr.getVersion()); + if (cipher == null) { + log.warn("Unable to find a matching cipher in previous key map"); + return null; + } else{ + if(log.isTraceEnabled()) + log.trace("decrypting using previous cipher version "+ hdr.getVersion()); + return _decrypt(cipher, msg, hdr.encrypt_entire_msg); + } + } + + else { + + // reset buffer with decrypted message + return _decrypt(cipher, msg, hdr.encrypt_entire_msg); + } + } + + + private static Message _decrypt(Cipher cipher, Message msg, boolean decrypt_entire_msg) throws Exception { + if(!decrypt_entire_msg) { + msg.setBuffer(cipher.doFinal(msg.getRawBuffer(), msg.getOffset(), msg.getLength())); + return msg; + } + + byte[] decrypted_msg=cipher.doFinal(msg.getRawBuffer(), msg.getOffset(), msg.getLength()); + Message ret=(Message)Util.streamableFromByteBuffer(Message.class, decrypted_msg); + if(ret.getDest() == null) + ret.setDest(msg.getDest()); + if(ret.getSrc() == null) + ret.setSrc(msg.getSrc()); + return ret; + } + + + /** + * @param secret + * @param pubKey + * @throws InvalidKeyException + * @throws IllegalStateException + * @throws IllegalBlockSizeException + * @throws BadPaddingException + */ + private void sendSecretKey(SecretKey secret, PublicKey pubKey, Address source) + throws InvalidKeyException, IllegalStateException, + IllegalBlockSizeException, BadPaddingException, NoSuchPaddingException, + NoSuchAlgorithmException + { + Message newMsg; + + if (log.isDebugEnabled()) + log.debug("encoding shared key "); + + // create a cipher with peer's public key + Cipher tmp = Cipher.getInstance(asymAlgorithm); + tmp.init(Cipher.ENCRYPT_MODE, pubKey); + + //encrypt current secret key + byte[] encryptedKey = tmp.doFinal(secret.getEncoded()); + + //SW logout encrypted bytes we are sending so we + // can match the clients log to see if they match + if (log.isDebugEnabled()) + log.debug(" Generated encoded key which only client can decode:" + + formatArray(encryptedKey)); + + newMsg = new Message(source, local_addr, encryptedKey); + + newMsg.putHeader(EncryptHeader.KEY, new EncryptHeader( + EncryptHeader.SECRETKEY, getSymVersion())); + + if (log.isDebugEnabled()) + log.debug(" Sending version " + getSymVersion() + + " encoded key to client"); + passItDown(new Event(Event.MSG, newMsg)); + } + + + /** + * @param msg + * @return + */ +// private PublicKey handleKeyRequest(Message msg) +// { +// Message newMsg; +// if (log.isDebugEnabled()) +// log.debug("Request for key recieved"); +// +// //SW log the clients encoded public key so we can +// // see if they match +// if (log.isDebugEnabled()) +// log.debug("Got peer's encoded public key:" +// + formatArray(msg.getBuffer())); +// +// PublicKey pubKey = generatePubKey(msg.getBuffer()); +// +// //SW log the clients resulting public key so we can +// // see if it is created correctly +// if (log.isDebugEnabled()) +// log.debug("Generated requestors public key" + pubKey); +// +// /* +// * SW why do we send this as the client does not use it ? - although we +// * could make use to provide some authentication later on rahter than +// * just encryption send server's publicKey +// */ +// newMsg = new Message(msg.getSrc(), local_addr, Kpair.getPublic() +// .getEncoded()); +// +// //SW Log out our public key in encoded format so we +// // can match with the client debugging to +// // see if they match +// if (log.isInfoEnabled()) +// log.debug("encoded key is " +// + formatArray(Kpair.getPublic().getEncoded())); +// +// +// newMsg.putHeader(EncryptHeader.KEY, new EncryptHeader( +// EncryptHeader.SERVER_PUBKEY, getSymVersion())); +// +// +// down_prot.down(new Event(Event.MSG, newMsg)); +// return pubKey; +// } + + + /** + * @return Message + */ + + private Message sendKeyRequest() + { + + // send client's public key to server and request + // server's public key + Message newMsg = new Message(keyServerAddr, local_addr, Kpair.getPublic() + .getEncoded()); + + newMsg.putHeader(EncryptHeader.KEY, new EncryptHeader( + EncryptHeader.KEY_REQUEST, getSymVersion())); + passItDown(new Event(Event.MSG, newMsg)); + return newMsg; + } + + + /* (non-Javadoc) + * @see org.jgroups.stack.Protocol#down(org.jgroups.Event) + */ + public Object down(Event evt) + { + if(observer != null) + observer.down(evt); + + switch (evt.getType()) { + + case Event.MSG : + try + { + if (queue_down){ + if(log.isTraceEnabled()) + log.trace("queueing down message as no session key established" + evt.getArg()); + downMessageQueue.put(evt); // queue messages if we are waiting for a new key + } else { + // make sure the down queue is drained first to keep ordering + if (!suppliedKey){ + drainDownQueue(); + } + sendDown(evt); + } + + } catch (Exception e) + { + log.warn("unable to send down event " + e); + } + return null; + + case Event.VIEW_CHANGE: + View view=(View)evt.getArg(); + if (log.isInfoEnabled()) + log.info("handling view-change down: " + view); + if (!suppliedKey){ + handleViewChange(view, false); + } + break; + case Event.TMP_VIEW: + view=(View)evt.getArg(); + if (log.isInfoEnabled()) + log.info("handling tmp-view down: " + view); + if (!suppliedKey){ + // if a tmp_view then we are trying to become coordinator so + // make us keyserver + handleViewChange(view, true); + } + break; + default : + break; + } + return down_prot.down(evt); + } + + public Object passItDown(Event evt) { + if(observer != null) + observer.passDown(evt); + return down_prot != null? down_prot.down(evt) : null; + } + + + /** + * @throws Exception + * @throws QueueClosedException + */ + private void drainDownQueue() throws Exception + { + // we do not synchronize here as we only have one down thread so we should never get an issue + // first lets replay any oustanding events + Event tmp =null; + while((tmp = (Event)downMessageQueue.poll(0L, TimeUnit.MILLISECONDS) )!= null){ + sendDown(tmp); + } + } + + + /** + * @param evt + * @throws Exception + */ + private void sendDown(Event evt) throws Exception { + if (evt.getType() != Event.MSG) { + return; + } + + Message msg = (Message) evt.getArg(); + if(msg.getLength() == 0) { + passItDown(evt); + return; + } + + EncryptHeader hdr=new EncryptHeader(EncryptHeader.ENCRYPT, getSymVersion()); + hdr.encrypt_entire_msg=this.encrypt_entire_message; + + if(encrypt_entire_message) { + byte[] serialized_msg=Util.streamableToByteBuffer(msg); + byte[] encrypted_msg=encryptMessage(symEncodingCipher, serialized_msg, 0, serialized_msg.length); + Message tmp=msg.copy(false); // we need to preserve headers which may already be present + tmp.setBuffer(encrypted_msg); + tmp.setSrc(local_addr); + tmp.putHeader(EncryptHeader.KEY, hdr); + passItDown(new Event(Event.MSG, tmp)); + return; + } + + + // put our encrypt header on the message + msg.putHeader(EncryptHeader.KEY, hdr); + + // copy neeeded because same message (object) may be retransmitted -> no double encryption + Message msgEncrypted = msg.copy(false); + msgEncrypted.setBuffer(encryptMessage(symEncodingCipher, msg.getRawBuffer(), msg.getOffset(), msg.getLength())); + passItDown(new Event(Event.MSG, msgEncrypted)); + } + + + /** + * + * @param cipher + * @param plain + * @return + * @throws Exception + */ + private static byte[] encryptMessage(Cipher cipher, byte[] plain, int offset, int length) throws Exception + { + return cipher.doFinal(plain, offset, length); + } + + + private SecretKeySpec decodeKey(byte[] encodedKey) throws Exception + { + // try and decode secrey key sent from keyserver + byte[] keyBytes = asymCipher.doFinal(encodedKey); + + SecretKeySpec keySpec = null; + try + { + keySpec = new SecretKeySpec(keyBytes, getAlgorithm(symAlgorithm)); + + // test reconstituted key to see if valid + Cipher temp = Cipher.getInstance(symAlgorithm); + temp.init(Cipher.SECRET_KEY, keySpec); + } catch (Exception e) + { + log.fatal(e); + keySpec = null; + } + return keySpec; + } + + + /** + * used to reconstitute public key sent in byte form from peer + * @param encodedKey + * @return PublicKey + */ + private PublicKey generatePubKey(byte[] encodedKey) + { + PublicKey pubKey = null; + try + { + KeyFactory KeyFac = KeyFactory.getInstance(getAlgorithm(asymAlgorithm)); + X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(encodedKey); + pubKey = KeyFac.generatePublic(x509KeySpec); + } catch (Exception e) + { + e.printStackTrace(); + } + return pubKey; + } + + + /* + * simple helper method so we can see the format of the byte arrays in a + * readable form could be better to use Base64 but will do for now + */ + private static String formatArray(byte[] array) + { + StringBuilder buf=new StringBuilder(); + for (int i = 0; i < array.length; i++) + { + buf.append(Integer.toHexString(array[i])); + } + return buf.toString(); + } + + + /** + * @return Returns the asymInit. + */ + protected int getAsymInit() + { + return asymInit; + } + + + /** + * @return Returns the asymProvider. + */ + protected String getAsymProvider() + { + return asymProvider; + } + + + /** + * @return Returns the desKey. + */ + protected SecretKey getDesKey() + { + return secretKey; + } + + + /** + * @return Returns the kpair. + */ + protected KeyPair getKpair() + { + return Kpair; + } + + + /** + * @return Returns the asymCipher. + */ + protected Cipher getAsymCipher() + { + return asymCipher; + } + + + /** + * @return Returns the serverPubKey. + */ + protected PublicKey getServerPubKey() + { + return serverPubKey; + } + + + /** + * @return Returns the symAlgorithm. + */ + protected String getSymAlgorithm() + { + return symAlgorithm; + } + + + /** + * @return Returns the symInit. + */ + protected int getSymInit() + { + return symInit; + } + + + /** + * @return Returns the symProvider. + */ + protected static String getSymProvider() + { + return symProvider; + } + + + /** + * @return Returns the asymAlgorithm. + */ + protected String getAsymAlgorithm() + { + return asymAlgorithm; + } + + + /** + * @return Returns the symVersion. + */ + private String getSymVersion() + { + return symVersion; + } + + + /** + * @param symVersion + * The symVersion to set. + */ + private void setSymVersion(String symVersion) + { + this.symVersion = symVersion; + } + + + /** + * @return Returns the secretKey. + */ + private SecretKey getSecretKey() + { + return secretKey; + } + + + /** + * @param secretKey The secretKey to set. + */ + private void setSecretKey(SecretKey secretKey) + { + this.secretKey = secretKey; + } + + + /** + * @return Returns the keyStoreName. + */ + protected String getKeyStoreName() + { + return keyStoreName; + } + /** + * @return Returns the symDecodingCipher. + */ + protected Cipher getSymDecodingCipher() + { + return symDecodingCipher; + } + /** + * @return Returns the symEncodingCipher. + */ + protected Cipher getSymEncodingCipher() + { + return symEncodingCipher; + } + /** + * @return Returns the local_addr. + */ + protected Address getLocal_addr() + { + return local_addr; + } + /** + * @param local_addr The local_addr to set. + */ + protected void setLocal_addr(Address local_addr) + { + this.local_addr = local_addr; + } + /** + * @return Returns the keyServerAddr. + */ + protected Address getKeyServerAddr() + { + return keyServerAddr; + } + /** + * @param keyServerAddr The keyServerAddr to set. + */ + protected void setKeyServerAddr(Address keyServerAddr) + { + this.keyServerAddr = keyServerAddr; + } + + + + public static class EncryptHeader extends org.jgroups.Header implements Streamable { + short type; + public static final short ENCRYPT = 0; + public static final short KEY_REQUEST = 1; + public static final short SERVER_PUBKEY = 2; + public static final short SECRETKEY = 3; + public static final short SECRETKEY_READY = 4; + + // adding key for Message object purpose + static final String KEY = "encrypt"; + + String version; + + boolean encrypt_entire_msg=false; + + + public EncryptHeader() + { + } + + + public EncryptHeader(short type) + { + //this(type, 0l); + this.type = type; + this.version = ""; + } + + + public EncryptHeader(short type, String version) + { + this.type = type; + this.version = version; + } + + + public void writeExternal(java.io.ObjectOutput out) throws IOException + { + out.writeShort(type); + out.writeObject(version); + } + + + public void readExternal(java.io.ObjectInput in) throws IOException, ClassNotFoundException + { + type = in.readShort(); + version = (String)in.readObject(); + } + + + public void writeTo(DataOutputStream out) throws IOException { + out.writeShort(type); + Util.writeString(version, out); + out.writeBoolean(encrypt_entire_msg); + } + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + type=in.readShort(); + version=Util.readString(in); + encrypt_entire_msg=in.readBoolean(); + } + + + public String toString() + { + return "ENCRYPT [type=" + type + " version=\"" + version + "\"]"; + } + + public int size() { + int retval=Global.SHORT_SIZE + Global.BYTE_SIZE + Global.BYTE_SIZE; + if(version != null) + retval+=version.length() +2; + return retval; + } + + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) + { + return obj instanceof EncryptHeader && ((((EncryptHeader)obj).getType() == type) && ((((EncryptHeader)obj) + .getVersion().equals(version)))); + } + + + /** + * @return Returns the type. + */ + protected short getType() + { + return type; + } + + + /** + * @return Returns the version. + */ + protected String getVersion() + { + return version; + } + } +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/EXAMPLE.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/EXAMPLE.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/EXAMPLE.java 17 Aug 2012 14:51:06 -0000 1.1 @@ -0,0 +1,90 @@ +// $Id: EXAMPLE.java,v 1.1 2012/08/17 14:51:06 marcin Exp $ + +package org.jgroups.protocols; + +import org.jgroups.Event; +import org.jgroups.Message; +import org.jgroups.View; +import org.jgroups.stack.Protocol; + +import java.io.Serializable; +import java.util.Vector; + + +class ExampleHeader implements Serializable { + private static final long serialVersionUID=-8802317525466899597L; + // your variables + + ExampleHeader() { + } + + public String toString() { + return "[EXAMPLE: ]"; + } +} + + +/** + * Example of a protocol layer. Contains no real functionality, can be used as a template. + */ + +public class EXAMPLE extends Protocol { + final Vector members=new Vector(); + + /** + * All protocol names have to be unique ! + */ + public String getName() { + return "EXAMPLE"; + } + + + /** + * Just remove if you don't need to reset any state + */ + public static void reset() { + } + + + public Object up(Event evt) { + Message msg; + + switch(evt.getType()) { + + case Event.MSG: + msg=(Message)evt.getArg(); + // Do something with the event, e.g. extract the message and remove a header. + // Optionally pass up + break; + } + + return up_prot.up(evt); // Pass up to the layer above us + } + + + public Object down(Event evt) { + + switch(evt.getType()) { + case Event.TMP_VIEW: + case Event.VIEW_CHANGE: + Vector new_members=((View)evt.getArg()).getMembers(); + synchronized(members) { + members.removeAllElements(); + if(new_members != null && !new_members.isEmpty()) + for(int i=0; i < new_members.size(); i++) + members.addElement(new_members.elementAt(i)); + } + return down_prot.down(evt); + + case Event.MSG: + Message msg=(Message)evt.getArg(); + // Do something with the event, e.g. add a header to the message + // Optionally pass down + break; + } + + return down_prot.down(evt); // Pass on to the layer below us + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/FC.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/FC.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/FC.java 17 Aug 2012 14:51:07 -0000 1.1 @@ -0,0 +1,957 @@ +package org.jgroups.protocols; + +import org.jgroups.*; +import org.jgroups.annotations.GuardedBy; +import org.jgroups.stack.Protocol; +import org.jgroups.util.BoundedList; +import org.jgroups.util.Streamable; +import org.jgroups.util.Util; + +import java.io.*; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + + +/** + * Simple flow control protocol based on a credit system. Each sender has a number of credits (bytes + * to send). When the credits have been exhausted, the sender blocks. Each receiver also keeps track of + * how many credits it has received from a sender. When credits for a sender fall below a threshold, + * the receiver sends more credits to the sender. Works for both unicast and multicast messages. + *

+ * Note that this protocol must be located towards the top of the stack, or all down_threads from JChannel to this + * protocol must be set to false ! This is in order to block JChannel.send()/JChannel.down(). + *
This is the second simplified implementation of the same model. The algorithm is sketched out in + * doc/FlowControl.txt + *
+ * Changes (Brian) April 2006: + *

    + *
  1. Receivers now send credits to a sender when more than min_credits have been received (rather than when min_credits + * are left) + *
  2. Receivers don't send the full credits (max_credits), but rather tha actual number of bytes received + *
      + * @author Bela Ban + * @version $Id: FC.java,v 1.1 2012/08/17 14:51:07 marcin Exp $ + */ +public class FC extends Protocol { + + /** + * Map: keys are members, values are credits left. For each send, the + * number of credits is decremented by the message size. A HashMap rather than a ConcurrentHashMap is + * currently used as there might be null values + */ + @GuardedBy("sent_lock") + private final Map sent=new HashMap(11); + // final Map sent=new ConcurrentHashMap(11); + + /** + * Map: keys are members, values are credits left (in bytes). + * For each receive, the credits for the sender are decremented by the size of the received message. + * When the credits are 0, we refill and send a CREDIT message to the sender. Sender blocks until CREDIT + * is received after reaching min_credits credits. + */ + @GuardedBy("received_lock") + private final Map received=new ConcurrentHashMap(11); + + + /** + * List of members from whom we expect credits + */ + @GuardedBy("sent_lock") + private final Set
      creditors=new HashSet
      (11); + + /** Peers who have asked for credit that we didn't have */ + private final Set
      pending_requesters=new HashSet
      (11); + + /** + * Max number of bytes to send per receiver until an ack must + * be received before continuing sending + */ + private long max_credits=500000; + + /** + * Max time (in milliseconds) to block. If credit hasn't been received after max_block_time, we send + * a REPLENISHMENT request to the members from which we expect credits. A value <= 0 means to + * wait forever. Note that when max_block_time elapses, we do not send the message, instead we send only + * a credit request. If we want a message to get sent after N milliseconds, use max_block_times instead. + */ + private long max_block_time=5000; + + /** + * Defines the max number of milliseconds for a message to block before being sent, based on the length of + * the message. The property is defined as a comma-separated list of values (separated by ':'), where the key + * is the size in bytes and the value is the number of milliseconds to block. + * Example: max_block_times="50:1,500:3,1500:5,10000:10,100000:100". This means that messages up to 50 bytes wait + * 1 ms max until they get sent, messages up to 500 bytes 3 ms, and so on. + * If a message's length (size of the payload in bytes) is for example 15'000 bytes, + * FC blocks it for a max of 100 ms. + */ + private Map max_block_times=null; + + /** Keeps track of the end time after which a message should not get blocked anymore */ + private static final ThreadLocal end_time=new ThreadLocal(); + + + /** + * If credits fall below this limit, we send more credits to the sender. (We also send when + * credits are exhausted (0 credits left)) + */ + private double min_threshold=0.25; + + /** + * Computed as max_credits times min_theshold. If explicitly set, this will + * override the above computation + */ + private long min_credits=0; + + /** + * Whether FC is still running, this is set to false when the protocol terminates (on stop()) + */ + private boolean running=true; + + + private boolean frag_size_received=false; + + + /** + * the lowest credits of any destination (sent_msgs) + */ + @GuardedBy("sent_lock") + private long lowest_credit=max_credits; + + /** Lock protecting sent credits table and some other vars (creditors for example) */ + private final Lock sent_lock=new ReentrantLock(); + + /** Lock protecting received credits table */ + private final Lock received_lock=new ReentrantLock(); + + + /** Mutex to block on down() */ + private final Condition credits_available=sent_lock.newCondition(); + + /** + * Whether an up thread that comes back down should be allowed to + * bypass blocking if all credits are exhausted. Avoids JGRP-465. + * Set to false by default in 2.5 because we have OOB messages for credit replenishments - this flag should not be set + * to true if the concurrent stack is used + */ + private boolean ignore_synchronous_response=true; + + /** + * Thread that carries messages through up() and shouldn't be blocked + * in down() if ignore_synchronous_response==true. JGRP-465. + */ + private final ThreadLocal ignore_thread=new ThreadLocal() { + protected Boolean initialValue() { + return false; + } + }; + + private static final String name="FC"; + + /** Last time a credit request was sent. Used to prevent credit request storms */ + @GuardedBy("sent_lock") + private long last_credit_request=0; + + private int num_blockings=0; + private int num_credit_requests_received=0, num_credit_requests_sent=0; + private int num_credit_responses_sent=0, num_credit_responses_received=0; + private long total_time_blocking=0; + + private final BoundedList last_blockings=new BoundedList(50); + + private final static FcHeader REPLENISH_HDR=new FcHeader(FcHeader.REPLENISH); + private final static FcHeader CREDIT_REQUEST_HDR=new FcHeader(FcHeader.CREDIT_REQUEST); + + + public final String getName() { + return name; + } + + public void resetStats() { + super.resetStats(); + num_blockings=0; + num_credit_responses_sent=num_credit_responses_received=num_credit_requests_received=num_credit_requests_sent=0; + total_time_blocking=0; + last_blockings.clear(); + } + + public long getMaxCredits() { + return max_credits; + } + + public void setMaxCredits(long max_credits) { + this.max_credits=max_credits; + } + + public double getMinThreshold() { + return min_threshold; + } + + public void setMinThreshold(double min_threshold) { + this.min_threshold=min_threshold; + } + + public long getMinCredits() { + return min_credits; + } + + public void setMinCredits(long min_credits) { + this.min_credits=min_credits; + } + + public int getNumberOfBlockings() { + return num_blockings; + } + + public long getMaxBlockTime() { + return max_block_time; + } + + public void setMaxBlockTime(long t) { + max_block_time=t; + } + + public long getTotalTimeBlocked() { + return total_time_blocking; + } + + public double getAverageTimeBlocked() { + return num_blockings == 0? 0.0 : total_time_blocking / (double)num_blockings; + } + + public int getNumberOfCreditRequestsReceived() { + return num_credit_requests_received; + } + + public int getNumberOfCreditRequestsSent() { + return num_credit_requests_sent; + } + + public int getNumberOfCreditResponsesReceived() { + return num_credit_responses_received; + } + + public int getNumberOfCreditResponsesSent() { + return num_credit_responses_sent; + } + + public String printSenderCredits() { + return printMap(sent); + } + + public String printReceiverCredits() { + return printMap(received); + } + + public String printCredits() { + StringBuilder sb=new StringBuilder(); + sb.append("senders:\n").append(printMap(sent)).append("\n\nreceivers:\n").append(printMap(received)); + return sb.toString(); + } + + public Map dumpStats() { + Map retval=super.dumpStats(); + if(retval == null) + retval=new HashMap(); + retval.put("senders", printMap(sent)); + retval.put("receivers", printMap(received)); + retval.put("num_blockings", this.num_blockings); + retval.put("avg_time_blocked", getAverageTimeBlocked()); + retval.put("num_replenishments", this.num_credit_responses_received); + retval.put("total_time_blocked", total_time_blocking); + retval.put("num_credit_requests", (long)num_credit_requests_sent); + return retval; + } + + public String showLastBlockingTimes() { + return last_blockings.toString(); + } + + + private long getMaxBlockTime(long length) { + if(max_block_times == null) + return 0; + Long retval=null; + for(Map.Entry entry: max_block_times.entrySet()) { + retval=entry.getValue(); + if(length <= entry.getKey()) + break; + } + + return retval != null? retval : 0; + } + + /** + * Allows to unblock a blocked sender from an external program, e.g. JMX + */ + public void unblock() { + sent_lock.lock(); + try { + if(log.isTraceEnabled()) + log.trace("unblocking the sender and replenishing all members, creditors are " + creditors); + + for(Map.Entry entry: sent.entrySet()) { + entry.setValue(max_credits); + } + + lowest_credit=computeLowestCredit(sent); + creditors.clear(); + credits_available.signalAll(); + } + finally { + sent_lock.unlock(); + } + } + + + public boolean setProperties(Properties props) { + String str; + boolean min_credits_set=false; + + super.setProperties(props); + str=props.getProperty("max_credits"); + if(str != null) { + max_credits=Long.parseLong(str); + props.remove("max_credits"); + } + + str=props.getProperty("min_threshold"); + if(str != null) { + min_threshold=Double.parseDouble(str); + props.remove("min_threshold"); + } + + str=props.getProperty("min_credits"); + if(str != null) { + min_credits=Long.parseLong(str); + props.remove("min_credits"); + min_credits_set=true; + } + + if(!min_credits_set) + min_credits=(long)((double)max_credits * min_threshold); + + str=props.getProperty("max_block_time"); + if(str != null) { + max_block_time=Long.parseLong(str); + props.remove("max_block_time"); + } + + str=props.getProperty("max_block_times"); + if(str != null) { + Long prev_key=null, prev_val=null; + List vals=Util.parseCommaDelimitedStrings(str); + max_block_times=new TreeMap(); + for(String tmp: vals) { + int index=tmp.indexOf(':'); + if(index == -1) + throw new IllegalArgumentException("element '" + tmp + "' is missing a ':' separator"); + Long key=Long.parseLong(tmp.substring(0, index).trim()); + Long val=Long.parseLong(tmp.substring(index +1).trim()); + + // sanity checks: + if(key < 0 || val < 0) + throw new IllegalArgumentException("keys and values must be >= 0"); + + if(prev_key != null) { + if(key <= prev_key) + throw new IllegalArgumentException("keys are not sorted: " + vals); + } + prev_key=key; + + if(prev_val != null) { + if(val <= prev_val) + throw new IllegalArgumentException("values are not sorted: " + vals); + } + prev_val=val; + max_block_times.put(key, val); + } + if(log.isDebugEnabled()) + log.debug("max_block_times: " + max_block_times); + props.remove("max_block_times"); + } + + Util.checkBufferSize("FC.max_credits", max_credits); + str=props.getProperty("ignore_synchronous_response"); + if(str != null) { + ignore_synchronous_response=Boolean.valueOf(str); + props.remove("ignore_synchronous_response"); + } + + if(!props.isEmpty()) { + log.error("the following properties are not recognized: " + props); + return false; + } + return true; + } + + public void start() throws Exception { + super.start(); + if(!frag_size_received) { + log.warn("No fragmentation protocol was found. When flow control (e.g. FC or SFC) is used, we recommend " + + "a fragmentation protocol, due to http://jira.jboss.com/jira/browse/JGRP-590"); + } + + sent_lock.lock(); + try { + running=true; + lowest_credit=max_credits; + } + finally { + sent_lock.unlock(); + } + } + + public void stop() { + super.stop(); + sent_lock.lock(); + try { + running=false; + ignore_thread.set(false); + credits_available.signalAll(); // notify all threads waiting on the mutex that we are done + } + finally { + sent_lock.unlock(); + } + } + + + public Object down(Event evt) { + switch(evt.getType()) { + case Event.MSG: + return handleDownMessage(evt); + case Event.INFO: + handleInfo((Map)evt.getArg()); + return null; + case Event.VIEW_CHANGE: + handleViewChange(((View)evt.getArg()).getMembers()); + break; + } + return down_prot.down(evt); // this could potentially use the lower protocol's thread which may block + } + + + public Object up(Event evt) { + switch(evt.getType()) { + + case Event.MSG: + // JGRP-465. We only deal with msgs to avoid having to use a concurrent collection; ignore views, + // suspicions, etc which can come up on unusual threads + Message msg=(Message)evt.getArg(); + FcHeader hdr=(FcHeader)msg.getHeader(name); + if(hdr != null) { + switch(hdr.type) { + case FcHeader.REPLENISH: + num_credit_responses_received++; + handleCredit(msg.getSrc(), (Number)msg.getObject()); + break; + case FcHeader.CREDIT_REQUEST: + num_credit_requests_received++; + Address sender=msg.getSrc(); + Long sent_credits=(Long)msg.getObject(); + handleCreditRequest(received, received_lock, sender, sent_credits); + break; + default: + log.error("header type " + hdr.type + " not known"); + break; + } + return null; // don't pass message up + } + + Address sender=msg.getSrc(); + long new_credits=adjustCredit(received, received_lock, sender, msg.getLength()); + + // JGRP-928: changed ignore_thread to a ThreadLocal: multiple threads can access it with the + // introduction of the concurrent stack + if(ignore_synchronous_response) + ignore_thread.set(true); + try { + return up_prot.up(evt); + } + finally { + if(ignore_synchronous_response) + ignore_thread.set(false); // need to revert because the thread is placed back into the pool + if(new_credits > 0) { + if(log.isTraceEnabled()) log.trace("sending " + new_credits + " credits to " + sender); + sendCredit(sender, new_credits); + } + } + + case Event.VIEW_CHANGE: + handleViewChange(((View)evt.getArg()).getMembers()); + break; + + case Event.INFO: + Map map=(Map)evt.getArg(); + handleInfo(map); + break; + } + return up_prot.up(evt); + } + + + private void handleInfo(Map info) { + if(info != null) { + Integer frag_size=(Integer)info.get("frag_size"); + if(frag_size != null) { + if(frag_size > max_credits) { + log.warn("The fragmentation size of the fragmentation protocol is " + frag_size + + ", which is greater than the max credits. While this is not incorrect, " + + "it may lead to long blockings. Frag size should be less than max_credits " + + "(http://jira.jboss.com/jira/browse/JGRP-590)"); + } + frag_size_received=true; + } + } + } + + private Object handleDownMessage(Event evt) { + Message msg=(Message)evt.getArg(); + int length=msg.getLength(); + Address dest=msg.getDest(); + + if(max_block_times != null) { + long tmp=getMaxBlockTime(length); + if(tmp > 0) + end_time.set(System.currentTimeMillis() + tmp); + } + + sent_lock.lock(); + try { + if(length > lowest_credit) { // then block and loop asking for credits until enough credits are available + + if(ignore_synchronous_response && ignore_thread.get()) { // JGRP-465 + if(log.isTraceEnabled()) + log.trace("bypassing blocking to avoid deadlocking " + Thread.currentThread()); + } + else { + determineCreditors(dest, length); + long start_blocking=System.currentTimeMillis(); + num_blockings++; // we count overall blockings, not blockings for *all* threads + if(log.isTraceEnabled()) + log.trace("Starting blocking. lowest_credit=" + lowest_credit + "; msg length =" + length); + + while(length > lowest_credit && running) { + try { + long block_time=max_block_time; + if(max_block_times != null) { + Long tmp=end_time.get(); + if(tmp != null) { + // Negative value means we don't wait at all ! If the end_time already elapsed + // (because we waited for other threads to get processed), the message will not + // block at all and get sent immediately + block_time=tmp - start_blocking; + } + } + + boolean rc=credits_available.await(block_time, TimeUnit.MILLISECONDS); + if(rc || length <= lowest_credit || !running) + break; + + // if we use max_block_times, then we do *not* send credit requests, even if we run + // into timeouts: in this case, it is up to the receivers to send new credits + if(!rc && max_block_times != null) + break; + + long wait_time=System.currentTimeMillis() - last_credit_request; + if(wait_time >= max_block_time) { + + // we have to set this var now, because we release the lock below (for sending a + // credit request), so all blocked threads would send a credit request, leading to + // a credit request storm + last_credit_request=System.currentTimeMillis(); + + // we need to send the credit requests down *without* holding the sent_lock, otherwise we might + // run into the deadlock described in http://jira.jboss.com/jira/browse/JGRP-292 + Map sent_copy=new HashMap(sent); + sent_copy.keySet().retainAll(creditors); + sent_lock.unlock(); + try { + // System.out.println(new Date() + " --> credit request"); + for(Map.Entry entry: sent_copy.entrySet()) { + sendCreditRequest(entry.getKey(), entry.getValue()); + } + } + finally { + sent_lock.lock(); + } + } + } + catch(InterruptedException e) { + // set the interrupted flag again, so the caller's thread can handle the interrupt as well + + // bela June 15 2007: don't do this as this will trigger an infinite loop !! + // (http://jira.jboss.com/jira/browse/JGRP-536) + // Thread.currentThread().interrupt(); + } + } + // if(!running) // don't send the message if not running anymore + // return null; + + long block_time=System.currentTimeMillis() - start_blocking; + if(log.isTraceEnabled()) + log.trace("total time blocked: " + block_time + " ms"); + total_time_blocking+=block_time; + last_blockings.add(block_time); + } + } + + long tmp=decrementCredit(sent, dest, length); + if(tmp != -1) + lowest_credit=Math.min(tmp, lowest_credit); + } + finally { + sent_lock.unlock(); + } + + // send message - either after regular processing, or after blocking (when enough credits available again) + return down_prot.down(evt); + } + + /** + * Checks whether one member (unicast msg) or all members (multicast msg) have enough credits. Add those + * that don't to the creditors list. Called with sent_lock held + * @param dest + * @param length + */ + private void determineCreditors(Address dest, int length) { + boolean multicast=dest == null || dest.isMulticastAddress(); + Address mbr; + Long credits; + if(multicast) { + for(Map.Entry entry: sent.entrySet()) { + mbr=entry.getKey(); + credits=entry.getValue(); + if(credits <= length) + creditors.add(mbr); + } + } + else { + credits=sent.get(dest); + if(credits != null && credits <= length) + creditors.add(dest); + } + } + + + /** + * Decrements credits from a single member, or all members in sent_msgs, depending on whether it is a multicast + * or unicast message. No need to acquire mutex (must already be held when this method is called) + * @param dest + * @param credits + * @return The lowest number of credits left, or -1 if a unicast member was not found + */ + private long decrementCredit(Map m, Address dest, long credits) { + boolean multicast=dest == null || dest.isMulticastAddress(); + long lowest=max_credits, new_credit; + Long val; + + if(multicast) { + if(m.isEmpty()) + return -1; + for(Map.Entry entry: m.entrySet()) { + val=entry.getValue(); + new_credit=val - credits; + entry.setValue(new_credit); + lowest=Math.min(new_credit, lowest); + } + return lowest; + } + else { + val=m.get(dest); + if(val != null) { + lowest=val; + lowest-=credits; + m.put(dest, lowest); + if(log.isTraceEnabled()) + log.trace("sender " + dest + " minus " + credits + + " credits, " + lowest + " remaining"); + return lowest; + } + } + return -1; + } + + + private void handleCredit(Address sender, Number increase) { + if(sender == null) return; + StringBuilder sb=null; + + sent_lock.lock(); + try { + Long old_credit=sent.get(sender); + if(old_credit == null) + return; + Long new_credit=Math.min(max_credits, old_credit + increase.longValue()); + + if(log.isTraceEnabled()) { + sb=new StringBuilder(); + sb.append("received credit from ").append(sender).append(", old credit was ").append(old_credit) + .append(", new credits are ").append(new_credit).append(".\nCreditors before are: ").append(creditors); + } + + sent.put(sender, new_credit); + lowest_credit=computeLowestCredit(sent); + // boolean was_empty=true; + if(!creditors.isEmpty()) { // we are blocked because we expect credit from one or more members + // was_empty=false; + creditors.remove(sender); + if(log.isTraceEnabled()) { + sb.append("\nCreditors after removal of ").append(sender).append(" are: ").append(creditors); + log.trace(sb); + } + } + if(creditors.isEmpty()) {// && !was_empty) { + credits_available.signalAll(); + } + } + finally { + sent_lock.unlock(); + } + } + + private static long computeLowestCredit(Map m) { + Collection credits=m.values(); // List of Longs (credits) + return Collections.min(credits); + } + + + /** + * Check whether sender has enough credits left. If not, send him some more + * @param map The hashmap to use + * @param lock The lock which can be used to lock map + * @param sender The address of the sender + * @param length The number of bytes received by this message. We don't care about the size of the headers for + * the purpose of flow control + * @return long Number of credits to be sent. Greater than 0 if credits needs to be sent, 0 otherwise + */ + private long adjustCredit(Map map, final Lock lock, Address sender, int length) { + if(sender == null) { + if(log.isErrorEnabled()) log.error("src is null"); + return 0; + } + + if(length == 0) + return 0; // no effect + + lock.lock(); + try { + long remaining_cred=decrementCredit(map, sender, length); + if(log.isTraceEnabled()) + log.trace("sender " + sender + " minus " + length + + " credits, " + remaining_cred + " remaining"); + if(remaining_cred == -1) + return 0; + long credit_response=max_credits - remaining_cred; + if(credit_response >= min_credits) { + map.put(sender, max_credits); + return credit_response; // this will trigger sending of new credits as we have received more than min_credits bytes from src + } + } + finally { + lock.unlock(); + } + return 0; + } + + /** + * @param map The map to modify + * @param lock The lock to lock map + * @param sender The sender who requests credits + * @param left_credits Number of bytes that the sender has left to send messages to us + */ + private void handleCreditRequest(Map map, Lock lock, Address sender, Long left_credits) { + if(sender == null) return; + long credit_response=0; + + lock.lock(); + try { + Long old_credit=map.get(sender); + if(old_credit != null) { + credit_response=Math.min(max_credits, max_credits - old_credit); + } + + if(credit_response > 0) { + if(log.isTraceEnabled()) + log.trace("received credit request from " + sender + ": sending " + credit_response + " credits"); + map.put(sender, max_credits); + pending_requesters.remove(sender); + } + else { + if(pending_requesters.contains(sender)) { + // a sender might have negative credits, e.g. -20000. If we subtracted -20000 from max_credits, + // we'd end up with max_credits + 20000, and send too many credits back. So if the sender's + // credits is negative, we simply send max_credits back + long credits_left=Math.max(0, left_credits.longValue()); + credit_response=max_credits - credits_left; + // credit_response = max_credits; + map.put(sender, max_credits); + pending_requesters.remove(sender); + if(log.isWarnEnabled()) + log.warn("Received two credit requests from " + sender + + " without any intervening messages; sending " + credit_response + " credits"); + } + else { + pending_requesters.add(sender); + if(log.isTraceEnabled()) + log.trace("received credit request from " + sender + " but have no credits available"); + } + } + } + finally { + lock.unlock(); + } + + if(credit_response > 0) + sendCredit(sender, credit_response); + } + + + private void sendCredit(Address dest, long credit) { + if(log.isTraceEnabled()) + log.trace("replentished " + dest + " with " + credit + + " credits"); + Number number; + if(credit < Integer.MAX_VALUE) + number=(int)credit; + else + number=credit; + Message msg=new Message(dest, null, number); + msg.setFlag(Message.OOB); + msg.putHeader(name, REPLENISH_HDR); + down_prot.down(new Event(Event.MSG, msg)); + num_credit_responses_sent++; + } + + /** + * We cannot send this request as OOB messages, as the credit request needs to queue up behind the regular messages; + * if a receiver cannot process the regular messages, that is a sign that the sender should be throttled ! + * @param dest The member to which we send the credit request + * @param credits_left The number of bytes (of credits) left for dest + */ + private void sendCreditRequest(final Address dest, Long credits_left) { + if(log.isTraceEnabled()) + log.trace("sending credit request to " + dest); + Message msg=new Message(dest, null, credits_left); + msg.putHeader(name, CREDIT_REQUEST_HDR); + down_prot.down(new Event(Event.MSG, msg)); + num_credit_requests_sent++; + } + + + private void handleViewChange(Vector
      mbrs) { + Address addr; + if(mbrs == null) return; + if(log.isTraceEnabled()) log.trace("new membership: " + mbrs); + + sent_lock.lock(); + received_lock.lock(); + try { + // add members not in membership to received and sent hashmap (with full credits) + for(int i=0; i < mbrs.size(); i++) { + addr=mbrs.elementAt(i); + if(!received.containsKey(addr)) + received.put(addr, max_credits); + if(!sent.containsKey(addr)) + sent.put(addr, max_credits); + } + // remove members that left + for(Iterator
      it=received.keySet().iterator(); it.hasNext();) { + addr=it.next(); + if(!mbrs.contains(addr)) + it.remove(); + } + + // remove members that left + for(Iterator
      it=sent.keySet().iterator(); it.hasNext();) { + addr=it.next(); + if(!mbrs.contains(addr)) + it.remove(); // modified the underlying map + } + + // remove all creditors which are not in the new view + /*for(Address creditor: creditors) { + if(!mbrs.contains(creditor)) + creditors.remove(creditor); + }*/ + // fixed http://jira.jboss.com/jira/browse/JGRP-754 (CCME) + for(Iterator
      it=creditors.iterator(); it.hasNext();) { + Address creditor=it.next(); + if(!mbrs.contains(creditor)) + it.remove(); + } + + if(log.isTraceEnabled()) log.trace("creditors are " + creditors); + if(creditors.isEmpty()) { + lowest_credit=computeLowestCredit(sent); + credits_available.signalAll(); + } + } + finally { + sent_lock.unlock(); + received_lock.unlock(); + } + } + + private static String printMap(Map m) { + StringBuilder sb=new StringBuilder(); + for(Map.Entry entry: m.entrySet()) { + sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); + } + return sb.toString(); + } + + + public static class FcHeader extends Header implements Streamable { + public static final byte REPLENISH=1; + public static final byte CREDIT_REQUEST=2; // the sender of the message is the requester + + byte type=REPLENISH; + private static final long serialVersionUID=8226510881574318828L; + + public FcHeader() { + + } + + public FcHeader(byte type) { + this.type=type; + } + + public int size() { + return Global.BYTE_SIZE; + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeByte(type); + } + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + type=in.readByte(); + } + + public void writeTo(DataOutputStream out) throws IOException { + out.writeByte(type); + } + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + type=in.readByte(); + } + + public String toString() { + switch(type) { + case REPLENISH: + return "REPLENISH"; + case CREDIT_REQUEST: + return "CREDIT_REQUEST"; + default: + return ""; + } + } + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/FD.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/FD.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/FD.java 17 Aug 2012 14:51:07 -0000 1.1 @@ -0,0 +1,698 @@ + +package org.jgroups.protocols; + + +import org.jgroups.*; +import org.jgroups.annotations.GuardedBy; +import org.jgroups.stack.Protocol; +import org.jgroups.util.*; + +import java.io.*; +import java.util.*; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + + +/** + * Failure detection based on simple heartbeat protocol. Regularly polls members for + * liveness. Multicasts SUSPECT messages when a member is not reachable. The simple + * algorithms works as follows: the membership is known and ordered. Each HB protocol + * periodically sends an 'are-you-alive' message to its *neighbor*. A neighbor is the next in + * rank in the membership list, which is recomputed upon a view change. When a response hasn't + * been received for n milliseconds and m tries, the corresponding member is suspected (and + * eventually excluded if faulty).

      + * FD starts when it detects (in a view change notification) that there are at least + * 2 members in the group. It stops running when the membership drops below 2.

      + * When a message is received from the monitored neighbor member, it causes the pinger thread to + * 'skip' sending the next are-you-alive message. Thus, traffic is reduced.

      + * When we receive a ping from a member that's not in the membership list, we shun it by sending it a + * NOT_MEMBER message. That member will then leave the group (and possibly rejoin). This is only done if + * shun is true. + * @author Bela Ban + * @version $Id: FD.java,v 1.1 2012/08/17 14:51:07 marcin Exp $ + */ +public class FD extends Protocol { + Address local_addr=null; + long timeout=3000; // number of millisecs to wait for an are-you-alive msg + long last_ack=System.currentTimeMillis(); + int num_tries=0; + int max_tries=2; // number of times to send a are-you-alive msg (tot time= max_tries*timeout) + + protected final Lock lock=new ReentrantLock(); + + @GuardedBy("lock") + Address ping_dest=null; + + @GuardedBy("lock") + final List

      members=new ArrayList
      (); + + /** Members from which we select ping_dest. may be subset of {@link #members} */ + @GuardedBy("lock") + final List
      pingable_mbrs=new ArrayList
      (); + + // number of pings from suspected mbrs + @GuardedBy("lock") + final Map invalid_pingers=new HashMap(7); + + boolean shun=true; + TimeScheduler timer=null; + + @GuardedBy("lock") + private Future monitor_future=null; // task that performs the actual monitoring for failure detection + + protected int num_heartbeats=0; + protected int num_suspect_events=0; + + /** Transmits SUSPECT message until view change or UNSUSPECT is received */ + protected final Broadcaster bcast_task=new Broadcaster(); + final static String name="FD"; + + final BoundedList
      suspect_history=new BoundedList
      (20); + + + + + + public String getName() {return name;} + public String getLocalAddress() {return local_addr != null? local_addr.toString() : "null";} + public String getMembers() {return members != null? members.toString() : "null";} + public String getPingableMembers() {return pingable_mbrs != null? pingable_mbrs.toString() : "null";} + public String getPingDest() {return ping_dest != null? ping_dest.toString() : "null";} + public int getNumberOfHeartbeatsSent() {return num_heartbeats;} + public int getNumSuspectEventsGenerated() {return num_suspect_events;} + public long getTimeout() {return timeout;} + public void setTimeout(long timeout) {this.timeout=timeout;} + public int getMaxTries() {return max_tries;} + public void setMaxTries(int max_tries) {this.max_tries=max_tries;} + public int getCurrentNumTries() {return num_tries;} + public boolean isShun() {return shun;} + public void setShun(boolean flag) {this.shun=flag;} + public String printSuspectHistory() { + StringBuilder sb=new StringBuilder(); + for(Address addr: suspect_history) { + sb.append(new Date()).append(": ").append(addr).append("\n"); + } + return sb.toString(); + } + + + public boolean setProperties(Properties props) { + String str; + + super.setProperties(props); + str=props.getProperty("timeout"); + if(str != null) { + timeout=Long.parseLong(str); + props.remove("timeout"); + } + + str=props.getProperty("max_tries"); // before suspecting a member + if(str != null) { + max_tries=Integer.parseInt(str); + props.remove("max_tries"); + } + + str=props.getProperty("shun"); + if(str != null) { + shun=Boolean.valueOf(str).booleanValue(); + props.remove("shun"); + } + + if(!props.isEmpty()) { + log.error("the following properties are not recognized: " + props); + return false; + } + return true; + } + + public void resetStats() { + num_heartbeats=num_suspect_events=0; + suspect_history.clear(); + } + + + public void init() throws Exception { + timer=getTransport().getTimer(); + if(timer == null) + throw new Exception("timer cannot be retrieved from protocol stack"); + } + + + public void stop() { + lock.lock(); + try { + stopMonitor(); + } + finally { + lock.unlock(); + } + } + + + private Address getPingDest(List
      mbrs) { + Address tmp, retval=null; + + if(mbrs == null || mbrs.size() < 2 || local_addr == null) + return null; + for(int i=0; i < mbrs.size(); i++) { + tmp=mbrs.get(i); + if(local_addr.equals(tmp)) { + if(i + 1 >= mbrs.size()) + retval=mbrs.get(0); + else + retval=mbrs.get(i + 1); + break; + } + } + return retval; + } + + /** Requires lock to held by caller */ + @GuardedBy("lock") + private void startMonitor() { + if(monitor_future == null || monitor_future.isDone()) { + last_ack=System.currentTimeMillis(); // start from scratch + monitor_future=timer.scheduleWithFixedDelay(new Monitor(), timeout, timeout, TimeUnit.MILLISECONDS); + num_tries=0; + } + } + + /** Requires lock to be held by caller */ + @GuardedBy("lock") + private void stopMonitor() { + if(monitor_future != null) { + monitor_future.cancel(true); + monitor_future=null; + } + } + + /** Restarts the monitor if the ping destination has changed. If not, this is a no-op. + * Requires lock to be held by the caller */ + @GuardedBy("lock") + private void restartMonitor() { + Address tmp_dest=getPingDest(pingable_mbrs); + boolean restart_monitor=tmp_dest == null || + ping_dest == null || // tmp_dest != null && ping_dest == null + !ping_dest.equals(tmp_dest); // tmp_dest != null && ping_dest != null + + if(restart_monitor) { + ping_dest=tmp_dest; + stopMonitor(); + if(ping_dest != null) { + try { + startMonitor(); + } + catch(Exception ex) { + if(log.isWarnEnabled()) log.warn("exception when calling startMonitor(): " + ex); + } + } + } + } + + + public Object up(Event evt) { + switch(evt.getType()) { + + case Event.SET_LOCAL_ADDRESS: + local_addr=(Address)evt.getArg(); + break; + + case Event.MSG: + Message msg=(Message)evt.getArg(); + FdHeader hdr=(FdHeader)msg.getHeader(name); + if(hdr == null) { + updateTimestamp(msg.getSrc()); + break; // message did not originate from FD layer, just pass up + } + + switch(hdr.type) { + case FdHeader.HEARTBEAT: // heartbeat request; send heartbeat ack + Address hb_sender=msg.getSrc(); + if(log.isTraceEnabled()) + log.trace("received are-you-alive from " + hb_sender + ", sending response"); + sendHeartbeatResponse(hb_sender); + + // 2. Shun the sender of a HEARTBEAT message if that sender is not a member. This will cause + // the sender to leave the group (and possibly rejoin it later) + if(shun) + shunInvalidHeartbeatSender(hb_sender); + break; // don't pass up ! + + case FdHeader.HEARTBEAT_ACK: // heartbeat ack + updateTimestamp(hdr.from); + break; + + case FdHeader.SUSPECT: + if(hdr.mbrs != null) { + if(log.isTraceEnabled()) log.trace("[SUSPECT] suspect hdr is " + hdr); + for(int i=0; i < hdr.mbrs.size(); i++) { + Address m=hdr.mbrs.elementAt(i); + if(local_addr != null && m.equals(local_addr)) { + if(log.isWarnEnabled()) + log.warn("I was suspected by " + msg.getSrc() + "; ignoring the SUSPECT " + + "message and sending back a HEARTBEAT_ACK"); + sendHeartbeatResponse(msg.getSrc()); + continue; + } + else { + lock.lock(); + try { + pingable_mbrs.remove(m); + restartMonitor(); + } + finally { + lock.unlock(); + } + } + up_prot.up(new Event(Event.SUSPECT, m)); + down_prot.down(new Event(Event.SUSPECT, m)); + } + } + break; + + case FdHeader.NOT_MEMBER: + if(shun) { + if(log.isDebugEnabled()) log.debug("[NOT_MEMBER] I'm being shunned; exiting"); + up_prot.up(new Event(Event.EXIT)); + } + break; + } + return null; + } + return up_prot.up(evt); // pass up to the layer above us + } + + + + + + public Object down(Event evt) { + switch(evt.getType()) { + case Event.VIEW_CHANGE: + down_prot.down(evt); + + lock.lock(); + try { + View v=(View)evt.getArg(); + members.clear(); + members.addAll(v.getMembers()); + bcast_task.adjustSuspectedMembers(members); + pingable_mbrs.clear(); + pingable_mbrs.addAll(members); + restartMonitor(); + } + finally { + lock.unlock(); + } + return null; + + case Event.UNSUSPECT: + unsuspect((Address)evt.getArg()); + return down_prot.down(evt); + } + return down_prot.down(evt); + } + + + private void sendHeartbeatResponse(Address dest) { + Message hb_ack=new Message(dest, null, null); + hb_ack.setFlag(Message.OOB); + FdHeader tmp_hdr=new FdHeader(FdHeader.HEARTBEAT_ACK); + tmp_hdr.from=local_addr; + hb_ack.putHeader(name, tmp_hdr); + down_prot.down(new Event(Event.MSG, hb_ack)); + } + + @GuardedBy("lock") + private void unsuspect(Address mbr) { + lock.lock(); + try { + bcast_task.removeSuspectedMember(mbr); + pingable_mbrs.clear(); + pingable_mbrs.addAll(members); + pingable_mbrs.removeAll(bcast_task.getSuspectedMembers()); + restartMonitor(); + } + finally { + lock.unlock(); + } + } + + @GuardedBy("lock") + private void updateTimestamp(Address sender) { + lock.lock(); + try { + if(ping_dest != null && sender != null && ping_dest.equals(sender)) { + last_ack=System.currentTimeMillis(); + if(log.isTraceEnabled()) + log.trace("received msg from " + sender + " (counts as ack)"); + num_tries=0; + } + } + finally { + lock.unlock(); + } + + } + + + /** + * If sender is not a member, send a NOT_MEMBER to sender (after n pings received) + */ + private void shunInvalidHeartbeatSender(Address hb_sender) { + int num_pings=0; + Message shun_msg=null; + + lock.lock(); + try { + if(hb_sender != null && members != null && !members.contains(hb_sender)) { + if(invalid_pingers.containsKey(hb_sender)) { + num_pings=invalid_pingers.get(hb_sender).intValue(); + if(num_pings >= max_tries) { + if(log.isDebugEnabled()) + log.debug(hb_sender + " is not in " + members + " ! Shunning it"); + shun_msg=new Message(hb_sender, null, null); + shun_msg.setFlag(Message.OOB); + shun_msg.putHeader(name, new FdHeader(FdHeader.NOT_MEMBER)); + invalid_pingers.remove(hb_sender); + } + else { + num_pings++; + invalid_pingers.put(hb_sender, new Integer(num_pings)); + } + } + else { + num_pings++; + invalid_pingers.put(hb_sender, new Integer(num_pings)); + } + } + } + finally { + lock.unlock(); + } + + if(shun_msg != null) + down_prot.down(new Event(Event.MSG, shun_msg)); + } + + + public static class FdHeader extends Header implements Streamable { + public static final byte HEARTBEAT=0; + public static final byte HEARTBEAT_ACK=1; + public static final byte SUSPECT=2; + public static final byte NOT_MEMBER=3; // received as response by pinged mbr when we are not a member + + + byte type=HEARTBEAT; + Vector
      mbrs=null; + Address from=null; // member who detected that suspected_mbr has failed + private static final long serialVersionUID=-6387039473828820899L; + + + public FdHeader() { + } // used for externalization + + public FdHeader(byte type) { + this.type=type; + } + + public FdHeader(byte type, Vector
      mbrs, Address from) { + this(type); + this.mbrs=mbrs; + this.from=from; + } + + + public String toString() { + switch(type) { + case HEARTBEAT: + return "heartbeat"; + case HEARTBEAT_ACK: + return "heartbeat ack"; + case SUSPECT: + return "SUSPECT (suspected_mbrs=" + mbrs + ", from=" + from + ")"; + case NOT_MEMBER: + return "NOT_MEMBER"; + default: + return "unknown type (" + type + ")"; + } + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeByte(type); + if(mbrs == null) + out.writeBoolean(false); + else { + out.writeBoolean(true); + out.writeInt(mbrs.size()); + for(Iterator it=mbrs.iterator(); it.hasNext();) { + Address addr=(Address)it.next(); + Marshaller.write(addr, out); + } + } + Marshaller.write(from, out); + } + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + type=in.readByte(); + boolean mbrs_not_null=in.readBoolean(); + if(mbrs_not_null) { + int len=in.readInt(); + mbrs=new Vector
      (11); + for(int i=0; i < len; i++) { + Address addr=(Address)Marshaller.read(in); + mbrs.add(addr); + } + } + from=(Address)Marshaller.read(in); + } + + + public int size() { + int retval=Global.BYTE_SIZE; // type + retval+=Util.size(mbrs); + retval+=Util.size(from); + return retval; + } + + + public void writeTo(DataOutputStream out) throws IOException { + out.writeByte(type); + Util.writeAddresses(mbrs, out); + Util.writeAddress(from, out); + } + + + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + type=in.readByte(); + mbrs=(Vector
      )Util.readAddresses(in, Vector.class); + from=Util.readAddress(in); + } + + } + + + protected class Monitor implements Runnable { + + public void run() { + Message hb_req; + long not_heard_from; // time in msecs we haven't heard from ping_dest + Address dest=null; + + lock.lock(); + try { + if(ping_dest == null) { + if(log.isWarnEnabled()) + log.warn("ping_dest is null: members=" + members + ", pingable_mbrs=" + + pingable_mbrs + ", local_addr=" + local_addr); + return; + } + else + dest=ping_dest; + } + finally { + lock.unlock(); + } + + + // 1. send heartbeat request + hb_req=new Message(dest, null, null); + hb_req.setFlag(Message.OOB); + hb_req.putHeader(name, new FdHeader(FdHeader.HEARTBEAT)); // send heartbeat request + if(log.isDebugEnabled()) + log.debug("sending are-you-alive msg to " + dest + " (own address=" + local_addr + ')'); + down_prot.down(new Event(Event.MSG, hb_req)); + num_heartbeats++; + + // 2. If the time of the last heartbeat is > timeout and max_tries heartbeat messages have not been + // received, then broadcast a SUSPECT message. Will be handled by coordinator, which may install + // a new view + not_heard_from=System.currentTimeMillis() - last_ack; + // quick & dirty fix: increase timeout by 500msecs to allow for latency (bela June 27 2003) + if(not_heard_from > timeout + 500) { // no heartbeat ack for more than timeout msecs + if(num_tries >= max_tries) { + if(log.isDebugEnabled()) + log.debug("[" + local_addr + "]: received no heartbeat ack from " + dest + + " for " + (num_tries +1) + " times (" + ((num_tries+1) * timeout) + + " milliseconds), suspecting it"); + // broadcast a SUSPECT message to all members - loop until + // unsuspect or view change is received + bcast_task.addSuspectedMember(dest); + num_tries=0; + if(stats) { + num_suspect_events++; + suspect_history.add(dest); + } + } + else { + if(log.isDebugEnabled()) + log.debug("heartbeat missing from " + dest + " (number=" + num_tries + ')'); + num_tries++; + } + } + } + } + + + /** + * Task that periodically broadcasts a list of suspected members to the group. Goal is not to lose + * a SUSPECT message: since these are bcast unreliably, they might get dropped. The BroadcastTask makes + * sure they are retransmitted until a view has been received which doesn't contain the suspected members + * any longer. Then the task terminates. + */ + protected final class Broadcaster { + final Vector
      suspected_mbrs=new Vector
      (7); + final Lock bcast_lock=new ReentrantLock(); + @GuardedBy("bcast_lock") + Future bcast_future=null; + @GuardedBy("bcast_lock") + BroadcastTask task; + + + Vector getSuspectedMembers() { + return suspected_mbrs; + } + + /** + * Starts a new task, or - if already running - adds the argument to the running task. + * @param suspect + */ + private void startBroadcastTask(Address suspect) { + bcast_lock.lock(); + try { + if(bcast_future == null || bcast_future.isDone()) { + task=new BroadcastTask(suspected_mbrs); + task.addSuspectedMember(suspect); + bcast_future=timer.scheduleWithFixedDelay(task, + 0, // run immediately the first time + timeout, // then every timeout milliseconds, until cancelled + TimeUnit.MILLISECONDS); + if(log.isTraceEnabled()) + log.trace("BroadcastTask started"); + } + else { + task.addSuspectedMember(suspect); + } + } + finally { + bcast_lock.unlock(); + } + } + + private void stopBroadcastTask() { + bcast_lock.lock(); + try { + if(bcast_future != null) { + bcast_future.cancel(true); + bcast_future=null; + task=null; + } + } + finally { + bcast_lock.unlock(); + } + } + + /** Adds a suspected member. Starts the task if not yet running */ + protected void addSuspectedMember(Address mbr) { + if(mbr == null) return; + if(!members.contains(mbr)) return; + synchronized(suspected_mbrs) { + if(!suspected_mbrs.contains(mbr)) { + suspected_mbrs.addElement(mbr); + startBroadcastTask(mbr); + } + } + } + + void removeSuspectedMember(Address suspected_mbr) { + if(suspected_mbr == null) return; + if(log.isDebugEnabled()) log.debug("member is " + suspected_mbr); + synchronized(suspected_mbrs) { + suspected_mbrs.removeElement(suspected_mbr); + if(suspected_mbrs.isEmpty()) + stopBroadcastTask(); + } + } + + + /** Removes all elements from suspected_mbrs that are not in the new membership */ + void adjustSuspectedMembers(List new_mbrship) { + if(new_mbrship == null || new_mbrship.isEmpty()) return; + synchronized(suspected_mbrs) { + suspected_mbrs.retainAll(new_mbrship); + if(suspected_mbrs.isEmpty()) + stopBroadcastTask(); + } + } + } + + + protected final class BroadcastTask implements Runnable { + private final Vector
      suspected_members=new Vector
      (); + + + BroadcastTask(Vector
      suspected_members) { + this.suspected_members.addAll(suspected_members); + } + + public void stop() { + suspected_members.clear(); + if(log.isTraceEnabled()) + log.trace("BroadcastTask stopped"); + } + + + public void run() { + Message suspect_msg; + FD.FdHeader hdr; + + synchronized(suspected_members) { + if(suspected_members.isEmpty()) { + stop(); + return; + } + + hdr=new FdHeader(FdHeader.SUSPECT); + hdr.mbrs=new Vector
      (suspected_members); + hdr.from=local_addr; + } + suspect_msg=new Message(); // mcast SUSPECT to all members + suspect_msg.setFlag(Message.OOB); + suspect_msg.putHeader(name, hdr); + if(log.isDebugEnabled()) + log.debug("broadcasting SUSPECT message [suspected_mbrs=" + suspected_members + "] to group"); + down_prot.down(new Event(Event.MSG, suspect_msg)); + } + + public void addSuspectedMember(Address suspect) { + if(suspect != null && !suspected_members.contains(suspect)) { + suspected_members.add(suspect); + } + } + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/FD_ALL.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/FD_ALL.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/FD_ALL.java 17 Aug 2012 14:51:05 -0000 1.1 @@ -0,0 +1,474 @@ +package org.jgroups.protocols; + +import org.jgroups.stack.Protocol; +import org.jgroups.*; +import org.jgroups.annotations.GuardedBy; +import org.jgroups.util.*; + +import java.util.*; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.io.*; + +/** + * Failure detection based on simple heartbeat protocol. Every member + * periodically multicasts a heartbeat. Every member also maintains a table of + * all members (minus itself). When data or a heartbeat from P are received, we + * reset the timestamp for P to the current time. Periodically, we check for + * expired members, and suspect those. + * + * @author Bela Ban + * @version $Id: FD_ALL.java,v 1.1 2012/08/17 14:51:05 marcin Exp $ + */ +public class FD_ALL extends Protocol { + /** Map of addresses and timestamps of last updates */ + final Map timestamps=new ConcurrentHashMap(); + + /** Number of milliseconds after which a HEARTBEAT is sent to the cluster */ + long interval=3000; + + /** + * Number of milliseconds after which a node P is suspected if neither a + * heartbeat nor data were received from P + */ + long timeout=5000; + + /** + * when a message is received from P, this is treated as if P sent a + * heartbeat + */ + boolean msg_counts_as_heartbeat=true; + + Address local_addr=null; + final List
      members=new ArrayList
      (); + + boolean shun=true; + TimeScheduler timer=null; + + // task which multicasts HEARTBEAT message after 'interval' ms + @GuardedBy("lock") + private ScheduledFuture heartbeat_sender_future=null; + + // task which checks for members exceeding timeout and suspects them + @GuardedBy("lock") + private ScheduledFuture timeout_checker_future=null; + + private boolean tasks_running=false; + + protected int num_heartbeats_sent, num_heartbeats_received=0; + protected int num_suspect_events=0; + + final static String name="FD_ALL"; + + final BoundedList
      suspect_history=new BoundedList
      (20); + final Map invalid_pingers=new HashMap(7); // keys=Address, val=Integer (number of pings from suspected mbrs) + + final Lock lock=new ReentrantLock(); + + public String getName() {return FD_ALL.name;} + public String getLocalAddress() {return local_addr != null? local_addr.toString() : "null";} + public String getMembers() {return members != null? members.toString() : "null";} + public int getHeartbeatsSent() {return num_heartbeats_sent;} + public int getHeartbeatsReceived() {return num_heartbeats_received;} + public int getSuspectEventsSent() {return num_suspect_events;} + public long getTimeout() {return timeout;} + public void setTimeout(long timeout) {this.timeout=timeout;} + public long getInterval() {return interval;} + public void setInterval(long interval) {this.interval=interval;} + public boolean isShun() {return shun;} + public void setShun(boolean flag) {this.shun=flag;} + public boolean isRunning() {return tasks_running;} + + public String printSuspectHistory() { + StringBuilder sb=new StringBuilder(); + for(Address tmp:suspect_history) { + sb.append(new Date()).append(": ").append(tmp).append("\n"); + } + return sb.toString(); + } + + public String printTimestamps() { + return printTimeStamps(); + } + + public boolean setProperties(Properties props) { + String str; + + super.setProperties(props); + str=props.getProperty("timeout"); + if(str != null) { + timeout=Long.parseLong(str); + props.remove("timeout"); + } + + str=props.getProperty("interval"); + if(str != null) { + interval=Long.parseLong(str); + props.remove("interval"); + } + + str=props.getProperty("shun"); + if(str != null) { + shun=Boolean.valueOf(str).booleanValue(); + props.remove("shun"); + } + + str=props.getProperty("msg_counts_as_heartbeat"); + if(str != null) { + msg_counts_as_heartbeat=Boolean.valueOf(str).booleanValue(); + props.remove("msg_counts_as_heartbeat"); + } + + if(!props.isEmpty()) { + log.error("the following properties are not recognized: " + props); + return false; + } + return true; + } + + public void resetStats() { + num_heartbeats_sent=num_heartbeats_received=num_suspect_events=0; + suspect_history.clear(); + } + + public void init() throws Exception { + timer=getTransport().getTimer(); + if(timer == null) + throw new Exception("timer cannot be retrieved from protocol stack"); + } + + public void stop() { + stopTasks(); + } + + public Object up(Event evt) { + Message msg; + Header hdr; + Address sender; + + switch(evt.getType()) { + + case Event.SET_LOCAL_ADDRESS: + local_addr=(Address)evt.getArg(); + break; + + case Event.MSG: + msg=(Message)evt.getArg(); + hdr=(Header)msg.getHeader(name); + if(msg_counts_as_heartbeat) + update(msg.getSrc()); // update when data is received too ? maybe a bit costly + if(hdr == null) + break; // message did not originate from FD_ALL layer, just pass up + + switch(hdr.type) { + case Header.HEARTBEAT: // heartbeat request; send heartbeat ack + sender=msg.getSrc(); + if(sender.equals(local_addr)) + break; + //if(log.isTraceEnabled()) + // log.trace(local_addr + ": received a heartbeat from " + sender); + + // 2. Shun the sender of a HEARTBEAT message if that sender is not a member. This will cause + // the sender to leave the group (and possibly rejoin it later) + if(shun && members != null && !members.contains(sender)) { + shunInvalidHeartbeatSender(sender); + break; + } + + update(sender); // updates the heartbeat entry for 'sender' + num_heartbeats_received++; + break; // don't pass up ! + + case Header.SUSPECT: + if(log.isTraceEnabled()) + log.trace("[SUSPECT] suspect hdr is " + hdr); + down_prot.down(new Event(Event.SUSPECT, hdr.suspected_mbr)); + up_prot.up(new Event(Event.SUSPECT, hdr.suspected_mbr)); + break; + + case Header.NOT_MEMBER: + if(shun) { + if(log.isDebugEnabled()) + log.debug("[NOT_MEMBER] I'm being shunned; exiting"); + up_prot.up(new Event(Event.EXIT)); + } + break; + } + return null; + } + return up_prot.up(evt); // pass up to the layer above us + } + + public Object down(Event evt) { + switch(evt.getType()) { + case Event.VIEW_CHANGE: + down_prot.down(evt); + View v=(View)evt.getArg(); + handleViewChange(v); + return null; + } + return down_prot.down(evt); + } + + private void startTasks() { + startHeartbeatSender(); + startTimeoutChecker(); + tasks_running=true; + if(log.isTraceEnabled()) + log.trace("started heartbeat sender and timeout checker tasks"); + } + + private void stopTasks() { + stopTimeoutChecker(); + stopHeartbeatSender(); + tasks_running=false; + if(log.isTraceEnabled()) + log.trace("stopped heartbeat sender and timeout checker tasks"); + } + + private void startTimeoutChecker() { + lock.lock(); + try { + if(timeout_checker_future == null || timeout_checker_future.isDone()) { + timeout_checker_future=timer.scheduleWithFixedDelay(new TimeoutChecker(), + interval, + interval, + TimeUnit.MILLISECONDS); + } + } + finally { + lock.unlock(); + } + } + + private void stopTimeoutChecker() { + lock.lock(); + try { + if(timeout_checker_future != null) { + timeout_checker_future.cancel(true); + timeout_checker_future=null; + } + } + finally { + lock.unlock(); + } + } + + private void startHeartbeatSender() { + lock.lock(); + try { + if(heartbeat_sender_future == null || heartbeat_sender_future.isDone()) { + heartbeat_sender_future=timer.scheduleWithFixedDelay(new HeartbeatSender(), + interval, + interval, + TimeUnit.MILLISECONDS); + } + } + finally { + lock.unlock(); + } + } + + private void stopHeartbeatSender() { + lock.lock(); + try { + if(heartbeat_sender_future != null) { + heartbeat_sender_future.cancel(true); + heartbeat_sender_future=null; + } + } + finally { + lock.unlock(); + } + } + + private void update(Address sender) { + if(sender != null && !sender.equals(local_addr)) + timestamps.put(sender, Long.valueOf(System.currentTimeMillis())); + } + + private void handleViewChange(View v) { + Vector
      mbrs=v.getMembers(); + members.clear(); + members.addAll(mbrs); + + Set
      keys=timestamps.keySet(); + keys.retainAll(mbrs); // remove all nodes which have left the cluster + for(Iterator
      it=mbrs.iterator();it.hasNext();) { // and add new members + Address mbr=it.next(); + if(mbr.equals(local_addr)) + continue; + if(!timestamps.containsKey(mbr)) { + timestamps.put(mbr, Long.valueOf(System.currentTimeMillis())); + } + } + + invalid_pingers.clear(); + + if(!tasks_running && members.size() > 1) + startTasks(); + else if(tasks_running && members.size() < 2) + stopTasks(); + } + + /** + * If sender is not a member, send a NOT_MEMBER to sender (after n pings + * received) + */ + private void shunInvalidHeartbeatSender(Address sender) { + int num_pings=0; + Message shun_msg; + + if(invalid_pingers.containsKey(sender)) { + num_pings=invalid_pingers.get(sender).intValue(); + if(num_pings >= 3) { + if(log.isDebugEnabled()) + log.debug(sender + " is not in " + members + " ! Shunning it"); + shun_msg=new Message(sender, null, null); + shun_msg.setFlag(Message.OOB); + shun_msg.putHeader(name, new Header(Header.NOT_MEMBER)); + down_prot.down(new Event(Event.MSG, shun_msg)); + invalid_pingers.remove(sender); + } + else { + num_pings++; + invalid_pingers.put(sender, new Integer(num_pings)); + } + } + else { + num_pings++; + invalid_pingers.put(sender, Integer.valueOf(num_pings)); + } + } + + private String printTimeStamps() { + StringBuilder sb=new StringBuilder(); + long current_time=System.currentTimeMillis(); + for(Iterator> it=timestamps.entrySet().iterator();it.hasNext();) { + Map.Entry entry=it.next(); + sb.append(entry.getKey()).append(": "); + sb.append(current_time - entry.getValue().longValue()).append(" ms old\n"); + } + return sb.toString(); + } + + void suspect(Address mbr) { + Message suspect_msg=new Message(); + suspect_msg.setFlag(Message.OOB); + Header hdr=new Header(Header.SUSPECT, mbr); + suspect_msg.putHeader(name, hdr); + down_prot.down(new Event(Event.MSG, suspect_msg)); + num_suspect_events++; + suspect_history.add(mbr); + } + + public static class Header extends org.jgroups.Header implements Streamable { + public static final byte HEARTBEAT=0; + public static final byte SUSPECT=1; + public static final byte NOT_MEMBER=2; // received as response by pinged mbr when we are not a member + + byte type=Header.HEARTBEAT; + Address suspected_mbr=null; + + /** used for externalization */ + public Header() {} + + public Header(byte type) { + this.type=type; + } + + public Header(byte type,Address suspect) { + this(type); + this.suspected_mbr=suspect; + } + + public String toString() { + switch(type) { + case FD_ALL.Header.HEARTBEAT: + return "heartbeat"; + case FD_ALL.Header.SUSPECT: + return "SUSPECT (suspected_mbr=" + suspected_mbr + ")"; + case FD_ALL.Header.NOT_MEMBER: + return "NOT_MEMBER"; + default: + return "unknown type (" + type + ")"; + } + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeByte(type); + out.writeObject(suspected_mbr); + } + + public void readExternal(ObjectInput in) throws IOException,ClassNotFoundException { + type=in.readByte(); + suspected_mbr=(Address)in.readObject(); + } + + public int size() { + int retval=Global.BYTE_SIZE; // type + retval+=Util.size(suspected_mbr); + return retval; + } + + public void writeTo(DataOutputStream out) throws IOException { + out.writeByte(type); + Util.writeAddress(suspected_mbr, out); + } + + public void readFrom(DataInputStream in) throws IOException, + IllegalAccessException, + InstantiationException { + type=in.readByte(); + suspected_mbr=Util.readAddress(in); + } + + } + + /** + * Class which periodically multicasts a HEARTBEAT message to the cluster + */ + class HeartbeatSender implements Runnable { + + public void run() { + Message heartbeat=new Message(); // send to all + heartbeat.setFlag(Message.OOB); + Header hdr=new Header(Header.HEARTBEAT); + heartbeat.putHeader(name, hdr); + down_prot.down(new Event(Event.MSG, heartbeat)); + num_heartbeats_sent++; + } + } + + class TimeoutChecker extends HeartbeatSender { + + public void run() { + if(log.isTraceEnabled()) + log.trace("checking for expired senders, table is:\n" + printTimeStamps()); + + long current_time=System.currentTimeMillis(), diff; + for(Iterator> it=timestamps.entrySet().iterator();it.hasNext();) { + Map.Entry entry=it.next(); + Address key=entry.getKey(); + Long val=entry.getValue(); + if(val == null) { + it.remove(); + continue; + } + diff=current_time - val.longValue(); + if(diff > timeout) { + if(log.isTraceEnabled()) + log.trace("haven't received a heartbeat from " + key + + " for " + + diff + + " ms, suspecting it"); + suspect(key); + } + } + } + } +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/FD_ICMP.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/FD_ICMP.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/FD_ICMP.java 17 Aug 2012 14:51:07 -0000 1.1 @@ -0,0 +1,176 @@ +package org.jgroups.protocols; + +import org.jgroups.Event; +import org.jgroups.Global; +import org.jgroups.stack.IpAddress; +import org.jgroups.util.Util; + +import java.lang.reflect.Method; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.UnknownHostException; +import java.util.Map; +import java.util.Properties; + +/** + * Protocol which uses InetAddress.isReachable() to check whether a given host is up or not, + * taking 1 argument; the host name of the host to be pinged. + * Note that this protocol only works with JDK 5 ! + * The implementation of this may or may not use ICMP ! An alternative is to create a TCP connection to port 7 (echo service) + * and see whether it works ! This is obviously done in JDK 5, so unless an echo service is configured to run, this + * won't work... + * @author Bela Ban + * @version $Id: FD_ICMP.java,v 1.1 2012/08/17 14:51:07 marcin Exp $ + */ +public class FD_ICMP extends FD { + + /** network interface to be used to send the ICMP packets */ + private NetworkInterface intf=null; + + private InetAddress bind_addr; + + private Method is_reacheable; + + /** Time-to-live for InetAddress.isReachable() */ + private int ttl=32; + + + + public String getName() { + return "FD_ICMP"; + } + + + public boolean setProperties(Properties props) { + boolean ignore_systemprops=Util.isBindAddressPropertyIgnored(); + String str=Util.getProperty(new String[]{Global.BIND_ADDR, Global.BIND_ADDR_OLD}, props, "bind_addr", + ignore_systemprops, null); + if(str != null) { + try { + bind_addr=InetAddress.getByName(str); + } + catch(UnknownHostException unknown) { + if(log.isFatalEnabled()) log.fatal("(bind_addr): host " + str + " not known"); + return false; + } + props.remove("bind_addr"); + } + + str=props.getProperty("ttl"); + if(str != null) { + ttl=Integer.parseInt(str); + props.remove("ttl"); + } + + super.setProperties(props); + + try { + Class is_reacheable_class=Util.loadClass("java.net.InetAddress", this.getClass()); + is_reacheable=is_reacheable_class.getMethod("isReachable", new Class[]{NetworkInterface.class, int.class, int.class}); + } + catch(ClassNotFoundException e) { + // log.error("failed checking for InetAddress.isReachable() method - requires JDK 5 or higher"); + Error error=new NoClassDefFoundError("failed checking for InetAddress.isReachable() method - requires JDK 5 or higher"); + error.initCause(e); + throw error; + } + catch(NoSuchMethodException e) { + // log.error("didn't find InetAddress.isReachable() method - requires JDK 5 or higher"); + Error error= new NoSuchMethodError("didn't find InetAddress.isReachable() method - requires JDK 5 or higher"); + error.initCause(e); + throw error; + } + + + if(!props.isEmpty()) { + log.error("the following properties are not recognized: " + props); + return false; + } + return true; + } + + + public void init() throws Exception { + super.init(); + if(bind_addr != null) + intf=NetworkInterface.getByInetAddress(bind_addr); + } + + public Object up(Event evt) { + switch(evt.getType()) { + case Event.CONFIG: + if(bind_addr == null) { + Map config=(Map)evt.getArg(); + bind_addr=(InetAddress)config.get("bind_addr"); + } + break; + } + return super.up(evt); + } + + + protected Monitor createMonitor() { + return new FD_ICMP.PingMonitor(); + } + + + /** + * Runs InetAddress.isReachable(). Each time the command fails, we increment num_tries. If num_tries > max_tries, we + * emit a SUSPECT message. If ping_dest changes, or we do receive traffic from ping_dest, we reset num_tries to 0. + */ + protected class PingMonitor extends Monitor { + long start, stop; + + public void run() { + if(ping_dest == null) { + if(log.isWarnEnabled()) + log.warn("ping_dest is null: members=" + members + ", pingable_mbrs=" + + pingable_mbrs + ", local_addr=" + local_addr); + return; + } + + // 1. execute ping command + InetAddress host=ping_dest instanceof IpAddress? ((IpAddress)ping_dest).getIpAddress() : null; + if(host == null) + throw new IllegalArgumentException("ping_dest is not of type IpAddress - FD_ICMP only works with these"); + try { + if(log.isTraceEnabled()) + log.trace("pinging " + host + " (ping_dest=" + ping_dest + ") using interface " + intf); + start=System.currentTimeMillis(); + Boolean rc=(Boolean)is_reacheable.invoke(host, new Object[]{intf, new Integer(ttl), new Integer((int)timeout)}); + stop=System.currentTimeMillis(); + num_heartbeats++; + if(rc.booleanValue()) { // success + num_tries=0; + if(log.isTraceEnabled()) + log.trace("successfully received response from " + host + " (after " + (stop-start) + "ms)"); + } + else { // failure + num_tries++; + if(log.isDebugEnabled()) + log.debug("could not ping " + ping_dest + " (tries=" + num_tries + ") after " + (stop-start) + "ms)"); + } + + if(num_tries >= max_tries) { + if(log.isDebugEnabled()) + log.debug("[" + local_addr + "]: could not ping " + ping_dest + " for " + (num_tries +1) + + " times (" + ((num_tries+1) * timeout) + " milliseconds), suspecting it"); + // broadcast a SUSPECT message to all members - loop until + // unsuspect or view change is received + bcast_task.addSuspectedMember(ping_dest); + num_tries=0; + if(stats) { + num_suspect_events++; + suspect_history.add(ping_dest); + } + } + } + catch(Exception ex) { + if(log.isErrorEnabled()) + log.error("failed pinging " + ping_dest, ex); + } + } + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/FD_PING.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/FD_PING.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/FD_PING.java 17 Aug 2012 14:51:06 -0000 1.1 @@ -0,0 +1,171 @@ +package org.jgroups.protocols; + +import org.apache.commons.logging.Log; +import org.jgroups.stack.IpAddress; +import org.jgroups.util.Util; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Properties; + +/** + * Protocol which uses an executable (e.g. /sbin/ping, or a script) to check whether a given host is up or not, + * taking 1 argument; the host name of the host to be pinged. Property 'cmd' determines the program to be executed + * (use a fully qualified name if the program is not on the path). + * @author Bela Ban + * @version $Id: FD_PING.java,v 1.1 2012/08/17 14:51:06 marcin Exp $ + */ +public class FD_PING extends FD { + /** Command (script or executable) to ping a host: a return value of 0 means success, anything else is a failure. + * The only argument passed to cmd is the host's address (symbolic name or dotted-decimal IP address) */ + String cmd="ping"; + + /** Write the stdout of the command to the log */ + boolean verbose=true; + + public String getName() { + return "FD_PING"; + } + + + public boolean setProperties(Properties props) { + String str; + str=props.getProperty("cmd"); + if(str != null) { + cmd=str; + props.remove("cmd"); + } + + str=props.getProperty("verbose"); + if(str != null) { + verbose=new Boolean(str).booleanValue(); + props.remove("verbose"); + } + + super.setProperties(props); + + if(props.size() > 0) { + log.error("the following properties are not recognized: " + props); + return false; + } + return true; + } + + protected Monitor createMonitor() { + return new PingMonitor(); + } + + + /** + * Executes the ping command. Each time the command fails, we increment num_tries. If num_tries > max_tries, we + * emit a SUSPECT message. If ping_dest changes, or we do receive traffic from ping_dest, we reset num_tries to 0. + */ + protected class PingMonitor extends Monitor { + + public void run() { + if(ping_dest == null) { + if(log.isWarnEnabled()) + log.warn("ping_dest is null: members=" + members + ", pingable_mbrs=" + + pingable_mbrs + ", local_addr=" + local_addr); + return; + } + + + // 1. execute ping command + String host=ping_dest instanceof IpAddress? ((IpAddress)ping_dest).getIpAddress().getHostAddress() : ping_dest.toString(); + String command=cmd + " " + host; + if(log.isDebugEnabled()) + log.debug("executing \"" + command + "\" (own address=" + local_addr + ')'); + try { + Log tmp_log=verbose? log : null; + int rc=Pinger.execute(command, tmp_log); + num_heartbeats++; + if(rc == 0) { // success + num_tries=0; + } + else { // failure + num_tries++; + if(log.isDebugEnabled()) + log.debug("could not ping " + ping_dest + " (tries=" + num_tries + ')'); + } + + if(num_tries >= max_tries) { + if(log.isDebugEnabled()) + log.debug("[" + local_addr + "]: could not ping " + ping_dest + " for " + (num_tries +1) + + " times (" + ((num_tries+1) * timeout) + " milliseconds), suspecting it"); + // broadcast a SUSPECT message to all members - loop until + // unsuspect or view change is received + bcast_task.addSuspectedMember(ping_dest); + num_tries=0; + if(stats) { + num_suspect_events++; + suspect_history.add(ping_dest); + } + } + } + catch(Exception ex) { + if(log.isErrorEnabled()) + log.error("failed executing command " + command, ex); + } + } + } + + + + protected static class Pinger { + + static int execute(String command, Log log) throws IOException, InterruptedException { + Process p=Runtime.getRuntime().exec(command); + InputStream in=p.getInputStream(), err=p.getErrorStream(); + try { + Reader in_reader, err_reader; + in_reader=new Reader(in, log); + err_reader=new Reader(err, log); + in_reader.start(); + err_reader.start(); + in_reader.join(); + err_reader.join(); + return p.exitValue(); + } + finally { + Util.close(in); + Util.close(err); + } + } + + + static class Reader extends Thread { + InputStreamReader in; + Log log=null; + boolean trace=false; + + Reader(InputStream in, Log log) { + this.in=new InputStreamReader(in); + this.log=log; + if(log != null) { + trace=log.isTraceEnabled(); + } + } + + public void run() { + int c; + StringBuilder sb=new StringBuilder(); + while(true) { + try { + c=in.read(); + if(c == -1) + break; + sb.append((char)c); + } + catch(IOException e) { + break; + } + } + if(log.isTraceEnabled()) + log.trace(sb.toString()); + } + } + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/FD_SIMPLE.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/FD_SIMPLE.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/FD_SIMPLE.java 17 Aug 2012 14:51:06 -0000 1.1 @@ -0,0 +1,375 @@ +// $Id: FD_SIMPLE.java,v 1.1 2012/08/17 14:51:06 marcin Exp $ + +package org.jgroups.protocols; + +import org.jgroups.*; +import org.jgroups.annotations.GuardedBy; +import org.jgroups.stack.Protocol; +import org.jgroups.util.Promise; +import org.jgroups.util.TimeScheduler; +import org.jgroups.util.Streamable; + +import java.io.*; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Properties; +import java.util.Vector; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + + +/** + * Simple failure detection protocol. Periodically sends a are-you-alive message to a randomly chosen member + * (excluding itself) and waits for a response. If a response has not been received within timeout msecs, a counter + * associated with that member will be incremented. If the counter exceeds max_missed_hbs, that member will be + * suspected. When a message or a heartbeat are received, the counter is reset to 0. + * + * @author Bela Ban Aug 2002 + * @version $Revision: 1.1 $ + */ +public class FD_SIMPLE extends Protocol { + Address local_addr=null; + TimeScheduler timer=null; + + final Lock heartbeat_lock=new ReentrantLock(); + @GuardedBy("heartbeat_lock") + Future heartbeat_future=null; + HeartbeatTask task; + + long interval=3000; // interval in msecs between are-you-alive messages + long timeout=3000; // time (in msecs) to wait for a response to are-you-alive + final Vector members=new Vector(); + final HashMap counters=new HashMap(); // keys=Addresses, vals=Integer (count) + int max_missed_hbs=5; // max number of missed responses until a member is suspected + static final String name="FD_SIMPLE"; + + + public String getName() { + return "FD_SIMPLE"; + } + + public void init() throws Exception { + timer=getTransport().getTimer(); + } + + public boolean setProperties(Properties props) { + String str; + + super.setProperties(props); + str=props.getProperty("timeout"); + if(str != null) { + timeout=Long.parseLong(str); + props.remove("timeout"); + } + + str=props.getProperty("interval"); + if(str != null) { + interval=Long.parseLong(str); + props.remove("interval"); + } + + str=props.getProperty("max_missed_hbs"); + if(str != null) { + max_missed_hbs=Integer.parseInt(str); + props.remove("max_missed_hbs"); + } + + if(!props.isEmpty()) { + log.error("the following properties are not recognized: " + props); + return false; + } + return true; + } + + + public void stop() { + heartbeat_lock.lock(); + try { + if(heartbeat_future != null) { + heartbeat_future.cancel(true); // we need to interrupt the thread as it may call wait() + heartbeat_future=null; + task=null; + } + } + finally { + heartbeat_lock.unlock(); + } + } + + + public Object up(Event evt) { + Message msg, rsp; + Address sender; + FdHeader hdr=null; + boolean counter_reset=false; + + switch(evt.getType()) { + + case Event.SET_LOCAL_ADDRESS: + local_addr=(Address)evt.getArg(); + break; + + case Event.MSG: + msg=(Message)evt.getArg(); + sender=msg.getSrc(); + resetCounter(sender); + counter_reset=true; + + hdr=(FdHeader)msg.getHeader(name); + if(hdr == null) + break; + + switch(hdr.type) { + case FdHeader.ARE_YOU_ALIVE: // are-you-alive request, send i-am-alive response + rsp=new Message(sender); + rsp.putHeader(name, new FdHeader(FdHeader.I_AM_ALIVE)); + down_prot.down(new Event(Event.MSG, rsp)); + return null; // don't pass up further + + case FdHeader.I_AM_ALIVE: + if(log.isInfoEnabled()) log.info("received I_AM_ALIVE response from " + sender); + heartbeat_lock.lock(); + try { + if(task != null) + task.receivedHeartbeatResponse(sender); + } + finally { + heartbeat_lock.unlock(); + } + if(!counter_reset) + resetCounter(sender); + return null; + + default: + if(log.isWarnEnabled()) log.warn("FdHeader type " + hdr.type + " not known"); + return null; + } + } + + return up_prot.up(evt); // pass up to the layer above us + } + + + public Object down(Event evt) { + View new_view; + Address key; + + switch(evt.getType()) { + + // Start heartbeat thread when we have more than 1 member; stop it when membership drops below 2 + case Event.VIEW_CHANGE: + new_view=(View)evt.getArg(); + members.clear(); + members.addAll(new_view.getMembers()); + if(new_view.size() > 1) { + heartbeat_lock.lock(); + try { + if(heartbeat_future == null || heartbeat_future.isDone()) { + task=new HeartbeatTask(); + if(log.isInfoEnabled()) log.info("starting heartbeat task"); + heartbeat_future=timer.scheduleWithFixedDelay(task, interval, interval, TimeUnit.MILLISECONDS); + } + } + finally { + heartbeat_lock.unlock(); + } + } + else { + heartbeat_lock.lock(); + try { + if(heartbeat_future != null) { + if(log.isInfoEnabled()) log.info("stopping heartbeat task"); + heartbeat_future.cancel(true); + heartbeat_future=null; + task=null; + } + } + finally { + heartbeat_lock.unlock(); + } + } + + // remove all keys from 'counters' which are not in this new view + synchronized(counters) { + for(Iterator it=counters.keySet().iterator(); it.hasNext();) { + key=(Address)it.next(); + if(!members.contains(key)) { + if(log.isInfoEnabled()) log.info("removing " + key + " from counters"); + it.remove(); + } + } + } + } + + return down_prot.down(evt); + } + + + + + + + + + /* -------------------------------- Private Methods ------------------------------- */ + + Address getHeartbeatDest() { + Address retval=null; + int r, size; + Vector members_copy; + + if(members == null || members.size() < 2 || local_addr == null) + return null; + members_copy=(Vector)members.clone(); + members_copy.removeElement(local_addr); // don't select myself as heartbeat destination + size=members_copy.size(); + r=((int)(Math.random() * (size + 1))) % size; + retval=(Address)members_copy.elementAt(r); + return retval; + } + + + int incrementCounter(Address mbr) { + Integer cnt; + int ret=0; + + if(mbr == null) return ret; + synchronized(counters) { + cnt=(Integer)counters.get(mbr); + if(cnt == null) { + cnt=new Integer(0); + counters.put(mbr, cnt); + } + else { + ret=cnt.intValue() + 1; + counters.put(mbr, new Integer(ret)); + } + return ret; + } + } + + + void resetCounter(Address mbr) { + if(mbr == null) return; + + synchronized(counters) { + counters.put(mbr, new Integer(0)); + } + } + + + String printCounters() { + StringBuilder sb=new StringBuilder(); + Address key; + + synchronized(counters) { + for(Iterator it=counters.keySet().iterator(); it.hasNext();) { + key=(Address)it.next(); + sb.append(key).append(": ").append(counters.get(key)).append('\n'); + } + } + return sb.toString(); + } + + /* ----------------------------- End of Private Methods --------------------------- */ + + + + + + + public static class FdHeader extends Header implements Streamable { + static final byte ARE_YOU_ALIVE=1; // sent periodically to a random member + static final byte I_AM_ALIVE=2; // response to above message + + + byte type=ARE_YOU_ALIVE; + private static final long serialVersionUID=4021056597004641352L; + + public FdHeader() { + } // used for externalization + + FdHeader(byte type) { + this.type=type; + } + + + public String toString() { + switch(type) { + case ARE_YOU_ALIVE: + return "[FD_SIMPLE: ARE_YOU_ALIVE]"; + case I_AM_ALIVE: + return "[FD_SIMPLE: I_AM_ALIVE]"; + default: + return "[FD_SIMPLE: unknown type (" + type + ")]"; + } + } + + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeByte(type); + } + + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + type=in.readByte(); + } + + public int size() { + return Global.BYTE_SIZE; + } + + public void writeTo(DataOutputStream out) throws IOException { + out.writeByte(type); + } + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + type=in.readByte(); + } + + + } + + + class HeartbeatTask implements Runnable { + final Promise
      promise=new Promise
      (); + Address dest=null; + + + public void receivedHeartbeatResponse(Address from) { + if(from != null && dest != null && from.equals(dest)) + promise.setResult(from); + } + + public void run() { + Message msg; + int num_missed_hbs=0; + + dest=getHeartbeatDest(); + if(dest == null) { + if(log.isWarnEnabled()) log.warn("heartbeat destination was null, will not send ARE_YOU_ALIVE message"); + return; + } + + if(log.isInfoEnabled()) + log.info("sending ARE_YOU_ALIVE message to " + dest + ", counters are\n" + printCounters()); + + promise.reset(); + msg=new Message(dest); + msg.putHeader(name, new FdHeader(FdHeader.ARE_YOU_ALIVE)); + down_prot.down(new Event(Event.MSG, msg)); + + promise.getResult(timeout); + num_missed_hbs=incrementCounter(dest); + if(num_missed_hbs >= max_missed_hbs) { + if(log.isInfoEnabled()) + log.info("missed " + num_missed_hbs + " from " + dest + ", suspecting member"); + up_prot.up(new Event(Event.SUSPECT, dest)); + } + } + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/FD_SOCK.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/FD_SOCK.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/FD_SOCK.java 17 Aug 2012 14:51:06 -0000 1.1 @@ -0,0 +1,1220 @@ + +package org.jgroups.protocols; + +import org.jgroups.*; +import org.jgroups.annotations.GuardedBy; +import org.jgroups.stack.IpAddress; +import org.jgroups.stack.Protocol; +import org.jgroups.util.*; +import org.jgroups.util.ThreadFactory; + +import java.io.*; +import java.net.*; +import java.util.*; +import java.util.concurrent.*; + + +/** + * Failure detection protocol based on sockets. Failure detection is ring-based. Each member creates a + * server socket and announces its address together with the server socket's address in a multicast. A + * pinger thread will be started when the membership goes above 1 and will be stopped when it drops below + * 2. The pinger thread connects to its neighbor on the right and waits until the socket is closed. When + * the socket is closed by the monitored peer in an abnormal fashion (IOException), the neighbor will be + * suspected.

      The main feature of this protocol is that no ping messages need to be exchanged between + * any 2 peers, and failure detection relies entirely on TCP sockets. The advantage is that no activity + * will take place between 2 peers as long as they are alive (i.e. have their server sockets open). + * The disadvantage is that hung servers or crashed routers will not cause sockets to be closed, therefore + * they won't be detected. + * The FD_SOCK protocol will work for groups where members are on different hosts

      + * The costs involved are 2 additional threads: one that + * monitors the client side of the socket connection (to monitor a peer) and another one that manages the + * server socket. However, those threads will be idle as long as both peers are running. + * @author Bela Ban May 29 2001 + * @version $Id: FD_SOCK.java,v 1.1 2012/08/17 14:51:06 marcin Exp $ + */ +public class FD_SOCK extends Protocol implements Runnable { + long get_cache_timeout=1000; // msecs to wait for the socket cache from the coordinator + long suspect_msg_interval=5000; // (BroadcastTask): mcast SUSPECT every 5000 msecs + int num_tries=3; // attempts coord is solicited for socket cache until we give up + final Vector

      members=new Vector
      (11); // list of group members (updated on VIEW_CHANGE) + boolean srv_sock_sent=false; // has own socket been broadcast yet ? + /** Used to rendezvous on GET_CACHE and GET_CACHE_RSP */ + final Promise> get_cache_promise=new Promise>(); + boolean got_cache_from_coord=false; // was cache already fetched ? + Address local_addr=null; // our own address + ServerSocket srv_sock=null; // server socket to which another member connects to monitor me + + InetAddress bind_addr=null; // the NIC on which the ServerSocket should listen + + String group_name=null; // the name of the group (set on CONNECT, nulled on DISCONNECT) + + private ServerSocketHandler srv_sock_handler=null; // accepts new connections on srv_sock + IpAddress srv_sock_addr=null; // pair of server_socket:port + Address ping_dest=null; // address of the member we monitor + Socket ping_sock=null; // socket to the member we monitor + InputStream ping_input=null; // input stream of the socket to the member we monitor + @GuardedBy("this") + volatile Thread pinger_thread=null; // listens on ping_sock, suspects member if socket is closed + + /** Cache of member addresses and their ServerSocket addresses */ + final ConcurrentMap cache=new ConcurrentHashMap(11); + + /** Start port for server socket (uses first available port starting at start_port). A value of 0 (default) + * picks a random port */ + int start_port=0; + final Promise ping_addr_promise=new Promise(); // to fetch the ping_addr for ping_dest + final Object sock_mutex=new Object(); // for access to ping_sock, ping_input + TimeScheduler timer=null; + private final BroadcastTask bcast_task=new BroadcastTask(); // to transmit SUSPECT message (until view change) + boolean regular_sock_close=false; // used by interruptPingerThread() when new ping_dest is computed + int num_suspect_events=0; + private static final int INTERRUPT =8; + private static final int NORMAL_TERMINATION=9; + private static final int ABNORMAL_TERMINATION=-1; + private static final String name="FD_SOCK"; + + final BoundedList
      suspect_history=new BoundedList
      (20); + + /** whether to use KEEP_ALIVE on the ping socket or not */ + private boolean keep_alive=true; + + private int sock_conn_timeout=3000; // max time in millis to wait for Socket.connect() to return + + private volatile boolean running=false; + + + public String getName() { + return name; + } + + public String getLocalAddress() {return local_addr != null? local_addr.toString() : "null";} + public String getMembers() {return members != null? members.toString() : "null";} + public String getPingableMembers() {return getMembers();} + public String getPingDest() {return ping_dest != null? ping_dest.toString() : "null";} + public int getNumSuspectEventsGenerated() {return num_suspect_events;} + public String printSuspectHistory() { + StringBuilder sb=new StringBuilder(); + for(Address suspect: suspect_history) { + sb.append(new Date()).append(": ").append(suspect).append("\n"); + } + return sb.toString(); + } + + public boolean setProperties(Properties props) { + String str; + + super.setProperties(props); + str=props.getProperty("get_cache_timeout"); + if(str != null) { + get_cache_timeout=Long.parseLong(str); + props.remove("get_cache_timeout"); + } + + str=props.getProperty("suspect_msg_interval"); + if(str != null) { + suspect_msg_interval=Long.parseLong(str); + props.remove("suspect_msg_interval"); + } + + str=props.getProperty("num_tries"); + if(str != null) { + num_tries=Integer.parseInt(str); + props.remove("num_tries"); + } + + str=props.getProperty("start_port"); + if(str != null) { + start_port=Integer.parseInt(str); + props.remove("start_port"); + } + + str=props.getProperty("keep_alive"); + if(str != null) { + keep_alive=Boolean.parseBoolean(str); + props.remove("keep_alive"); + } + + str=props.getProperty("srv_sock_bind_addr"); + if(str != null) { + log.error("srv_sock_bind_addr is deprecated and will be ignored - use bind_addr instead"); + props.remove("srv_sock_bind_addr"); + } + + str=props.getProperty("sock_conn_timeout"); + if(str != null) { + sock_conn_timeout=Integer.parseInt(str); + props.remove("sock_conn_timeout"); + } + + try { + bind_addr=Util.getBindAddress(props); + } + catch(UnknownHostException unknown) { + log.fatal("failed getting bind_addr", unknown); + return false; + } + catch(SocketException ex) { + log.fatal("failed getting bind_addr", ex); + return false; + } + + if(!props.isEmpty()) { + log.error("the following properties are not recognized: " + props); + return false; + } + return true; + } + + + public String printCache() { + StringBuilder sb=new StringBuilder(); + for(Map.Entry entry: cache.entrySet()) { + sb.append(entry.getKey()).append(" has server socket at ").append(entry.getValue()).append("\n"); + } + return sb.toString(); + } + + + public void init() throws Exception { + srv_sock_handler=new ServerSocketHandler(); + timer=getTransport().getTimer(); + if(timer == null) + throw new Exception("timer is null"); + } + + + public void start() throws Exception { + super.start(); + startServerSocket(); + running=true; + } + + public void stop() { + running=false; + bcast_task.removeAll(); + synchronized(this) { + stopPingerThread(); + } + stopServerSocket(true); // graceful close + } + + public void resetStats() { + super.resetStats(); + num_suspect_events=0; + suspect_history.clear(); + } + + + public Object up(Event evt) { + switch(evt.getType()) { + + case Event.SET_LOCAL_ADDRESS: + local_addr=(Address) evt.getArg(); + break; + + case Event.MSG: + Message msg=(Message) evt.getArg(); + FdHeader hdr=(FdHeader)msg.getHeader(name); + if(hdr == null) + break; // message did not originate from FD_SOCK layer, just pass up + + switch(hdr.type) { + + case FdHeader.SUSPECT: + if(hdr.mbrs != null) { + if(log.isDebugEnabled()) log.debug("[SUSPECT] hdr=" + hdr); + for(Address m: hdr.mbrs) { + if(local_addr != null && m.equals(local_addr)) { + if(log.isWarnEnabled()) + log.warn("I was suspected by " + msg.getSrc() + "; ignoring the SUSPECT message"); + continue; + } + up_prot.up(new Event(Event.SUSPECT, m)); + down_prot.down(new Event(Event.SUSPECT, m)); + } + } + else + if(log.isWarnEnabled()) log.warn("[SUSPECT]: hdr.mbrs == null"); + break; + + // If I have the sock for 'hdr.mbr', return it. Otherwise look it up in my cache and return it + case FdHeader.WHO_HAS_SOCK: + if(local_addr != null && local_addr.equals(msg.getSrc())) + return null; // don't reply to WHO_HAS bcasts sent by me ! + + if(hdr.mbr == null) { + if(log.isErrorEnabled()) log.error("hdr.mbr is null"); + return null; + } + + if(log.isTraceEnabled()) log.trace("who-has-sock " + hdr.mbr); + + // 1. Try my own address, maybe it's me whose socket is wanted + if(local_addr != null && local_addr.equals(hdr.mbr) && srv_sock_addr != null) { + sendIHaveSockMessage(msg.getSrc(), local_addr, srv_sock_addr); // unicast message to msg.getSrc() + return null; + } + + // 2. If I don't have it, maybe it is in the cache + IpAddress addr=cache.get(hdr.mbr); + if(addr != null) + sendIHaveSockMessage(msg.getSrc(), hdr.mbr, addr); // ucast msg + break; + + + // Update the cache with the addr:sock_addr entry (if on the same host) + case FdHeader.I_HAVE_SOCK: + if(hdr.mbr == null || hdr.sock_addr == null) { + if(log.isErrorEnabled()) log.error("[I_HAVE_SOCK]: hdr.mbr is null or hdr.sock_addr == null"); + return null; + } + + // if(!cache.containsKey(hdr.mbr)) + cache.put(hdr.mbr, hdr.sock_addr); // update the cache + if(log.isTraceEnabled()) log.trace("i-have-sock: " + hdr.mbr + " --> " + + hdr.sock_addr + " (cache is " + cache + ')'); + + if(ping_dest != null && hdr.mbr.equals(ping_dest)) + ping_addr_promise.setResult(hdr.sock_addr); + break; + + // Return the cache to the sender of this message + case FdHeader.GET_CACHE: + Address sender=msg.getSrc(); // guaranteed to be non-null + hdr=new FdHeader(FdHeader.GET_CACHE_RSP,new HashMap(cache)); + msg=new Message(sender, null, null); + msg.setFlag(Message.OOB); + msg.putHeader(name, hdr); + down_prot.down(new Event(Event.MSG, msg)); + break; + + case FdHeader.GET_CACHE_RSP: + if(hdr.cachedAddrs == null) { + if(log.isWarnEnabled()) log.warn("(GET_CACHE_RSP): cache is null"); + return null; + } + get_cache_promise.setResult(hdr.cachedAddrs); + break; + } + return null; + + case Event.CONFIG: + if(bind_addr == null) { + Map config=(Map)evt.getArg(); + bind_addr=(InetAddress)config.get("bind_addr"); + } + break; + } + + return up_prot.up(evt); // pass up to the layer above us + } + + + public Object down(Event evt) { + switch(evt.getType()) { + + case Event.UNSUSPECT: + bcast_task.removeSuspectedMember((Address)evt.getArg()); + break; + + case Event.DISCONNECT: + stopServerSocket(true); // graceful close + break; + + case Event.SHUTDOWN: + stopServerSocket(false); + break; + + case Event.VIEW_CHANGE: + View v=(View) evt.getArg(); + final Vector
      new_mbrs=v.getMembers(); + + Runnable reshuffleSockets=new Runnable() { + public void run() { + synchronized(FD_SOCK.this) { + members.removeAllElements(); + members.addAll(new_mbrs); + cache.keySet().retainAll(members); // remove all entries in 'cache' which are not in the new membership + bcast_task.adjustSuspectedMembers(members); + if(log.isDebugEnabled()) log.debug("VIEW_CHANGE received: " + members); + + if(members.size() > 1) { + if(pinger_thread != null && pinger_thread.isAlive()) { + Address tmp_ping_dest=determinePingDest(); + if(ping_dest != null && tmp_ping_dest != null && !ping_dest.equals(tmp_ping_dest)) { + interruptPingerThread(); // allows the thread to use the new socket + } + } + else + startPingerThread(); // only starts if not yet running + } + else { + ping_dest=null; + stopPingerThread(); + } + } + } + }; + timer.submit(reshuffleSockets); + break; + + default: + return down_prot.down(evt); + } + + return down_prot.down(evt); + } + + + /** + * Runs as long as there are 2 members and more. Determines the member to be monitored and fetches its + * server socket address (if n/a, sends a message to obtain it). The creates a client socket and listens on + * it until the connection breaks. If it breaks, emits a SUSPECT message. It the connection is closed regularly, + * nothing happens. In both cases, a new member to be monitored will be chosen and monitoring continues (unless + * there are fewer than 2 members). + */ + public void run() { + if(log.isTraceEnabled()) log.trace("pinger_thread started"); + + // 1. Broadcast my own addr:sock to all members so they can update their cache + if(!srv_sock_sent) { + if(srv_sock_addr != null) { + sendIHaveSockMessage(null, // send to all members + local_addr, + srv_sock_addr); + srv_sock_sent=true; + } + else + if(log.isWarnEnabled()) log.warn("(VIEW_CHANGE): srv_sock_addr == null"); + } + + // 2. Get the addr:pid cache from the coordinator (only if not already fetched) + if(!got_cache_from_coord) { + getCacheFromCoordinator(); + got_cache_from_coord=true; + } + + while(pinger_thread != null && Thread.currentThread().equals(pinger_thread) && running) { + ping_dest=determinePingDest(); // gets the neighbor to our right + if(log.isDebugEnabled()) + log.debug("determinePingDest()=" + ping_dest); + if(ping_dest == null) { + break; + } + IpAddress ping_addr=fetchPingAddress(ping_dest); + if(ping_addr == null) { + if(!running) + break; + if(log.isErrorEnabled()) log.error("socket address for " + ping_dest + " could not be fetched, retrying"); + Util.sleep(1000); + continue; + } + + if(!setupPingSocket(ping_addr)) { + // covers use cases #7 and #8 in ManualTests.txt + if(log.isDebugEnabled()) log.debug("could not create socket to " + ping_dest + "; suspecting " + ping_dest); + broadcastSuspectMessage(ping_dest); + continue; + } + + if(log.isDebugEnabled()) log.debug("ping_dest=" + ping_dest + ", ping_sock=" + ping_sock + ", cache=" + cache); + + // at this point ping_input must be non-null, otherwise setupPingSocket() would have thrown an exception + try { + if(ping_input != null) { + int c=ping_input.read(); + switch(c) { + case NORMAL_TERMINATION: + if(log.isDebugEnabled()) + log.debug("peer closed socket normally"); + synchronized(this) { + pinger_thread=null; + } + break; + case ABNORMAL_TERMINATION: + handleSocketClose(null); + break; + default: + break; + } + } + } + catch(IOException ex) { // we got here when the peer closed the socket --> suspect peer and then continue + handleSocketClose(ex); + } + catch(Throwable catch_all_the_rest) { + log.error("exception", catch_all_the_rest); + } + } + if(log.isDebugEnabled()) log.debug("pinger thread terminated"); + synchronized(this) { + pinger_thread=null; + } + } + + + + + /* ----------------------------------- Private Methods -------------------------------------- */ + + + void handleSocketClose(Exception ex) { + teardownPingSocket(); // make sure we have no leftovers + if(!regular_sock_close) { // only suspect if socket was not closed regularly (by interruptPingerThread()) + if(log.isDebugEnabled()) + log.debug("peer " + ping_dest + " closed socket (" + (ex != null ? ex.getClass().getName() : "eof") + ')'); + broadcastSuspectMessage(ping_dest); + } + else { + if(log.isDebugEnabled()) log.debug("socket to " + ping_dest + " was reset"); + regular_sock_close=false; + } + } + + + /** + * Does *not* need to be synchronized on pinger_mutex because the caller (down()) already has the mutex acquired + */ + void startPingerThread() { + running=true; + if(pinger_thread == null) { + ThreadFactory factory=getThreadFactory(); + pinger_thread=factory.newThread(this, "FD_SOCK pinger"); + pinger_thread.setDaemon(true); + pinger_thread.start(); + } + } + + + void stopPingerThread() { + running=false; + if(pinger_thread != null && pinger_thread.isAlive()) { + regular_sock_close=true; + pinger_thread=null; + sendPingTermination(); // PATCH by Bruce Schuchardt (http://jira.jboss.com/jira/browse/JGRP-246) + teardownPingSocket(); + ping_addr_promise.setResult(null); + get_cache_promise.setResult(null); + } + + } + + // PATCH: send something so the connection handler can exit + void sendPingTermination() { + sendPingSignal(NORMAL_TERMINATION); + } + + void sendPingInterrupt() { + sendPingSignal(INTERRUPT); + } + + + void sendPingSignal(int signal) { + synchronized(sock_mutex) { + if(ping_sock != null) { + try { + OutputStream out=ping_sock.getOutputStream(); + if(out != null) { + out.write(signal); + out.flush(); + } + } + catch(Throwable t) { + if(log.isTraceEnabled()) + log.trace("problem sending signal " + signalToString(signal), t); + } + } + } + } + + + + + /** + * Interrupts the pinger thread. The Thread.interrupt() method doesn't seem to work under Linux with JDK 1.3.1 + * (JDK 1.2.2 had no problems here), therefore we close the socket (setSoLinger has to be set !) if we are + * running under Linux. This should be tested under Windows. (Solaris 8 and JDK 1.3.1 definitely works).

      + * Oct 29 2001 (bela): completely removed Thread.interrupt(), but used socket close on all OSs. This makes this + * code portable and we don't have to check for OSs.

      + * Does *not* need to be synchronized on pinger_mutex because the caller (down()) already has the mutex acquired + * @see {@link org.jgroups.tests.InterruptTest} to determine whether Thread.interrupt() works for InputStream.read(). + */ + void interruptPingerThread() { + if(pinger_thread != null && pinger_thread.isAlive()) { + regular_sock_close=true; + sendPingInterrupt(); // PATCH by Bruce Schuchardt (http://jira.jboss.com/jira/browse/JGRP-246) + teardownPingSocket(); // will wake up the pinger thread. less elegant than Thread.interrupt(), but does the job + } + } + + void startServerSocket() throws IOException { + srv_sock=Util.createServerSocket(bind_addr, start_port); // grab a random unused port above 10000 + srv_sock_addr=new IpAddress(bind_addr, srv_sock.getLocalPort()); + if(srv_sock_handler != null) { + srv_sock_handler.start(); // won't start if already running + } + } + + void stopServerSocket(boolean graceful) { + if(srv_sock_handler != null) + srv_sock_handler.stop(graceful); + } + + + /** + * Creates a socket to dest, and assigns it to ping_sock. Also assigns ping_input + */ + boolean setupPingSocket(IpAddress dest) { + synchronized(sock_mutex) { + if(dest == null) { + if(log.isErrorEnabled()) log.error("destination address is null"); + return false; + } + try { + SocketAddress destAddr=new InetSocketAddress(dest.getIpAddress(), dest.getPort()); + ping_sock=new Socket(); + ping_sock.setSoLinger(true, 1); + ping_sock.setKeepAlive(keep_alive); + ping_sock.connect(destAddr, sock_conn_timeout); + ping_input=ping_sock.getInputStream(); + return true; + } + catch(Throwable ex) { + return false; + } + } + } + + + void teardownPingSocket() { + synchronized(sock_mutex) { + if(ping_sock != null) { + try { + ping_sock.shutdownInput(); + ping_sock.close(); + } + catch(Exception ex) { + } + ping_sock=null; + } + Util.close(ping_input); + ping_input=null; + } + } + + + /** + * Determines coordinator C. If C is null and we are the first member, return. Else loop: send GET_CACHE message + * to coordinator and wait for GET_CACHE_RSP response. Loop until valid response has been received. + */ + void getCacheFromCoordinator() { + Address coord; + int attempts=num_tries; + Message msg; + FdHeader hdr; + Map result; + + get_cache_promise.reset(); + while(attempts > 0 && running) { + if((coord=determineCoordinator()) != null) { + if(coord.equals(local_addr)) { // we are the first member --> empty cache + if(log.isDebugEnabled()) log.debug("first member; cache is empty"); + return; + } + hdr=new FdHeader(FdHeader.GET_CACHE); + msg=new Message(coord, null, null); + msg.setFlag(Message.OOB); + msg.putHeader(name, hdr); + down_prot.down(new Event(Event.MSG, msg)); + result=get_cache_promise.getResult(get_cache_timeout); + if(result != null) { + cache.putAll(result); + if(log.isTraceEnabled()) log.trace("got cache from " + coord + ": cache is " + cache); + return; + } + else { + if(log.isWarnEnabled()) log.warn("received null cache; retrying"); + } + } + + --attempts; + } + } + + + /** + * Sends a SUSPECT message to all group members. Only the coordinator (or the next member in line if the coord + * itself is suspected) will react to this message by installing a new view. To overcome the unreliability + * of the SUSPECT message (it may be lost because we are not above any retransmission layer), the following scheme + * is used: after sending the SUSPECT message, it is also added to the broadcast task, which will periodically + * re-send the SUSPECT until a view is received in which the suspected process is not a member anymore. The reason is + * that - at one point - either the coordinator or another participant taking over for a crashed coordinator, will + * react to the SUSPECT message and issue a new view, at which point the broadcast task stops. + */ + void broadcastSuspectMessage(Address suspected_mbr) { + Message suspect_msg; + FdHeader hdr; + + if(suspected_mbr == null) return; + + if(log.isTraceEnabled()) log.trace("suspecting " + suspected_mbr + " (own address is " + local_addr + ')'); + + // 1. Send a SUSPECT message right away; the broadcast task will take some time to send it (sleeps first) + hdr=new FdHeader(FdHeader.SUSPECT); + hdr.mbrs=new HashSet

      (1); + hdr.mbrs.add(suspected_mbr); + suspect_msg=new Message(); + suspect_msg.setFlag(Message.OOB); + suspect_msg.putHeader(name, hdr); + down_prot.down(new Event(Event.MSG, suspect_msg)); + + // 2. Add to broadcast task and start latter (if not yet running). The task will end when + // suspected members are removed from the membership + bcast_task.addSuspectedMember(suspected_mbr); + if(stats) { + num_suspect_events++; + suspect_history.add(suspected_mbr); + } + } + + + + + /** + Sends or broadcasts a I_HAVE_SOCK response. If 'dst' is null, the reponse will be broadcast, otherwise + it will be unicast back to the requester + */ + void sendIHaveSockMessage(Address dst, Address mbr, IpAddress addr) { + Message msg=new Message(dst, null, null); + msg.setFlag(Message.OOB); + FdHeader hdr=new FdHeader(FdHeader.I_HAVE_SOCK); + hdr.mbr=mbr; + hdr.sock_addr=addr; + msg.putHeader(name, hdr); + down_prot.down(new Event(Event.MSG, msg)); + } + + + /** + Attempts to obtain the ping_addr first from the cache, then by unicasting q request to mbr, + then by multicasting a request to all members. + */ + private IpAddress fetchPingAddress(Address mbr) { + IpAddress ret; + Message ping_addr_req; + FdHeader hdr; + + if(mbr == null) { + if(log.isErrorEnabled()) log.error("mbr == null"); + return null; + } + // 1. Try to get the server socket address from the cache + if((ret=cache.get(mbr)) != null) + return ret; + + // 2. Try to get the server socket address from mbr + ping_addr_promise.reset(); + ping_addr_req=new Message(mbr, null, null); // unicast + ping_addr_req.setFlag(Message.OOB); + hdr=new FdHeader(FdHeader.WHO_HAS_SOCK); + hdr.mbr=mbr; + ping_addr_req.putHeader(name, hdr); + down_prot.down(new Event(Event.MSG, ping_addr_req)); + if(!running) return null; + ret=ping_addr_promise.getResult(500); + if(ret != null) { + return ret; + } + + // 3. Try to get the server socket address from all members + ping_addr_req=new Message(null); // multicast + ping_addr_req.setFlag(Message.OOB); + hdr=new FdHeader(FdHeader.WHO_HAS_SOCK); + hdr.mbr=mbr; + ping_addr_req.putHeader(name, hdr); + down_prot.down(new Event(Event.MSG, ping_addr_req)); + ret=ping_addr_promise.getResult(500); + return ret; + } + + + private synchronized Address determinePingDest() { + Vector
      non_suspected_mbrs=new Vector
      (members); + non_suspected_mbrs.removeAll(bcast_task.getSuspectedMembers()); + return determineRightNeighbor(non_suspected_mbrs); + } + + private synchronized Address determineRightNeighbor(Vector
      list) { + Address result=null; + if(list.size() > 1 && local_addr != null) { + if(local_addr.equals(list.lastElement())) { + result=list.firstElement(); + } + else { + int myIndex=list.indexOf(local_addr); + if(myIndex != -1){ + result=list.get(myIndex + 1); + } + } + } + return result; + } + + + Address determineCoordinator() { + return !members.isEmpty()? members.elementAt(0) : null; + } + + + static String signalToString(int signal) { + switch(signal) { + case NORMAL_TERMINATION: return "NORMAL_TERMINATION"; + case ABNORMAL_TERMINATION: return "ABNORMAL_TERMINATION"; + case INTERRUPT: return "INTERRUPT"; + default: return "n/a"; + } + } + + + + + /* ------------------------------- End of Private Methods ------------------------------------ */ + + + public static class FdHeader extends Header implements Streamable { + public static final byte SUSPECT=10; + public static final byte WHO_HAS_SOCK=11; + public static final byte I_HAVE_SOCK=12; + public static final byte GET_CACHE=13; // sent by joining member to coordinator + public static final byte GET_CACHE_RSP=14; // sent by coordinator to joining member in response to GET_CACHE + + + byte type=SUSPECT; + Address mbr=null; // set on WHO_HAS_SOCK (requested mbr), I_HAVE_SOCK + IpAddress sock_addr; // set on I_HAVE_SOCK + Map cachedAddrs=null; // set on GET_CACHE_RSP + Set
      mbrs=null; // set on SUSPECT (list of suspected members) + private static final long serialVersionUID=-7025890133989522764L; + + + public FdHeader() { + } // used for externalization + + public FdHeader(byte type) { + this.type=type; + } + + public FdHeader(byte type, Address mbr) { + this.type=type; + this.mbr=mbr; + } + + public FdHeader(byte type, Address mbr, IpAddress sock_addr) { + this.type=type; + this.mbr=mbr; + this.sock_addr = sock_addr ; + } + + public FdHeader(byte type, Set
      mbrs) { + this.type=type; + this.mbrs=mbrs; + } + + public FdHeader(byte type, Map cachedAddrs) { + this.type=type; + this.cachedAddrs=cachedAddrs; + } + + + public String toString() { + StringBuilder sb=new StringBuilder(); + sb.append(type2String(type)); + if(mbr != null) + sb.append(", mbr=").append(mbr); + if(sock_addr != null) + sb.append(", sock_addr=").append(sock_addr); + if(cachedAddrs != null) + sb.append(", cache=").append(cachedAddrs); + if(mbrs != null) + sb.append(", mbrs=").append(mbrs); + return sb.toString(); + } + + + public static String type2String(byte type) { + switch(type) { + case SUSPECT: + return "SUSPECT"; + case WHO_HAS_SOCK: + return "WHO_HAS_SOCK"; + case I_HAVE_SOCK: + return "I_HAVE_SOCK"; + case GET_CACHE: + return "GET_CACHE"; + case GET_CACHE_RSP: + return "GET_CACHE_RSP"; + default: + return "unknown type (" + type + ')'; + } + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeByte(type); + out.writeObject(mbr); + out.writeObject(sock_addr); + out.writeObject(cachedAddrs); + out.writeObject(mbrs); + } + + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + type=in.readByte(); + mbr=(Address)in.readObject(); + sock_addr=(IpAddress)in.readObject(); + cachedAddrs=(Map)in.readObject(); + mbrs=(Set
      )in.readObject(); + } + + public int size() { + int retval=Global.BYTE_SIZE; // type + retval+=Util.size(mbr); + + // use of Util.size(Address) with IpAddress overestimates size by one byte. + // replace: retval+=Util.size(sock_addr); with the following: + int ipaddr_size = 0 ; + ipaddr_size += Global.BYTE_SIZE ; // presence byte + if (sock_addr != null) + ipaddr_size += sock_addr.size(); // IpAddress size + retval += ipaddr_size ; + + retval+=Global.INT_SIZE; // cachedAddrs size + Address key; + IpAddress val; + if(cachedAddrs != null) { + for(Map.Entry entry: cachedAddrs.entrySet()) { + if((key=entry.getKey()) != null) + retval+=Util.size(key); + retval+=Global.BYTE_SIZE; // presence for val + if((val=entry.getValue()) != null) + retval+=val.size(); + } + } + + retval+=Global.INT_SIZE; // mbrs size + if(mbrs != null) { + for(Address m: mbrs) { + retval+=Util.size(m); + } + } + + return retval; + } + + public void writeTo(DataOutputStream out) throws IOException { + int size; + out.writeByte(type); + Util.writeAddress(mbr, out); + Util.writeStreamable(sock_addr, out); + size=cachedAddrs != null? cachedAddrs.size() : 0; + out.writeInt(size); + if(size > 0) { + for(Map.Entry entry: cachedAddrs.entrySet()) { + Address key=entry.getKey(); + IpAddress val=entry.getValue(); + Util.writeAddress(key, out); + Util.writeStreamable(val, out); + } + } + size=mbrs != null? mbrs.size() : 0; + out.writeInt(size); + if(size > 0) { + for(Address address: mbrs) { + Util.writeAddress(address, out); + } + } + } + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + int size; + type=in.readByte(); + mbr=Util.readAddress(in); + sock_addr=(IpAddress)Util.readStreamable(IpAddress.class, in); + size=in.readInt(); + if(size > 0) { + if(cachedAddrs == null) + cachedAddrs=new HashMap(size); + for(int i=0; i < size; i++) { + Address key=Util.readAddress(in); + IpAddress val=(IpAddress)Util.readStreamable(IpAddress.class, in); + cachedAddrs.put(key, val); + } + } + size=in.readInt(); + if(size > 0) { + if(mbrs == null) + mbrs=new HashSet
      (); + for(int i=0; i < size; i++) { + Address addr=Util.readAddress(in); + mbrs.add(addr); + } + } + } + + } + + + /** + * Handles the server-side of a client-server socket connection. Waits until a client connects, and then loops + * until that client closes the connection. Note that there is no new thread spawned for the listening on the + * client socket, therefore there can only be 1 client connection at the same time. Subsequent clients attempting + * to create a connection will be blocked until the first client closes its connection. This should not be a problem + * as the ring nature of the FD_SOCK protocol always has only 1 client connect to its right-hand-side neighbor. + */ + private class ServerSocketHandler implements Runnable { + Thread acceptor=null; + /** List */ + final List clients=new LinkedList(); + + + String getName() { + return acceptor != null? acceptor.getName() : null; + } + + ServerSocketHandler() { + start(); + } + + final void start() { + if(acceptor == null) { + acceptor=getThreadFactory().newThread(this, "FD_SOCK server socket acceptor"); + acceptor.setDaemon(true); + acceptor.start(); + } + } + + + final void stop(boolean graceful) { + if(acceptor != null && acceptor.isAlive()) { + try { + srv_sock.close(); // this will terminate thread, peer will receive SocketException (socket close) + } + catch(Exception ex) { + } + } + synchronized(clients) { + for(ClientConnectionHandler handler: clients) { + handler.stopThread(graceful); + } + clients.clear(); + } + acceptor=null; + } + + + /** Only accepts 1 client connection at a time (saving threads) */ + public void run() { + Socket client_sock; + while(acceptor != null && srv_sock != null) { + try { + if(log.isTraceEnabled()) // +++ remove + log.trace("waiting for client connections on " + srv_sock.getInetAddress() + ":" + + srv_sock.getLocalPort()); + client_sock=srv_sock.accept(); + if(log.isTraceEnabled()) // +++ remove + log.trace("accepted connection from " + client_sock.getInetAddress() + ':' + client_sock.getPort()); + ClientConnectionHandler client_conn_handler=new ClientConnectionHandler(client_sock, clients); + Thread t = getThreadFactory().newThread(client_conn_handler, "FD_SOCK client connection handler"); + t.setDaemon(true); + + synchronized(clients) { + clients.add(client_conn_handler); + } + t.start(); + } + catch(IOException io_ex2) { + break; + } + } + acceptor=null; + } + } + + + + /** Handles a client connection; multiple client can connect at the same time */ + private static class ClientConnectionHandler implements Runnable { + Socket client_sock=null; + InputStream in; + final Object mutex=new Object(); + final List clients; + + ClientConnectionHandler(Socket client_sock, List clients) { + this.client_sock=client_sock; + this.clients=clients; + } + + void stopThread(boolean graceful) { + synchronized(mutex) { + if(client_sock != null) { + try { + if(graceful) { + OutputStream out=client_sock.getOutputStream(); + out.write(NORMAL_TERMINATION); + out.flush(); + } + closeClientSocket(); + } + catch(Throwable t) { + } + } + } + } + + private void closeClientSocket() { + synchronized(mutex) { + Util.close(client_sock); + client_sock=null; + } + } + + public void run() { + try { + synchronized(mutex) { + if(client_sock == null) + return; + in=client_sock.getInputStream(); + } + int b=0; + do { + b=in.read(); + } + while(b != ABNORMAL_TERMINATION && b != NORMAL_TERMINATION); + } + catch(IOException ex) { + } + finally { + Socket sock=client_sock; // PATCH: avoid race condition causing NPE + if (sock != null && !sock.isClosed()) + closeClientSocket(); + synchronized(clients) { + clients.remove(this); + } + } + } + } + + + /** + * Task that periodically broadcasts a list of suspected members to the group. Goal is not to lose + * a SUSPECT message: since these are bcast unreliably, they might get dropped. The BroadcastTask makes + * sure they are retransmitted until a view has been received which doesn't contain the suspected members + * any longer. Then the task terminates. + */ + private class BroadcastTask implements Runnable { + final Set
      suspected_mbrs=new HashSet
      (); + Future future; + + + public Set
      getSuspectedMembers() { + return Collections.unmodifiableSet(suspected_mbrs); + } + + /** Adds a suspected member. Starts the task if not yet running */ + public void addSuspectedMember(Address mbr) { + if(mbr == null) return; + if(!members.contains(mbr)) return; + synchronized(suspected_mbrs) { + if(suspected_mbrs.add(mbr)) + startTask(); + } + } + + public boolean isSuspectedMember(Address member){ + synchronized(suspected_mbrs) { + return suspected_mbrs.contains(member); + } + } + + + public void removeSuspectedMember(Address suspected_mbr) { + if(suspected_mbr == null) return; + if(log.isDebugEnabled()) log.debug("member is " + suspected_mbr); + synchronized(suspected_mbrs) { + suspected_mbrs.remove(suspected_mbr); + if(suspected_mbrs.isEmpty()) { + stopTask(); + } + } + } + + + public void removeAll() { + synchronized(suspected_mbrs) { + suspected_mbrs.clear(); + stopTask(); + } + } + + + private void startTask() { + if(future == null || future.isDone()) { + try { + future=timer.scheduleWithFixedDelay(this, suspect_msg_interval, suspect_msg_interval, TimeUnit.MILLISECONDS); + } + catch(RejectedExecutionException e) { + if(log.isWarnEnabled()) + log.warn("task " + this + " was rejected as timer thread pool is shutting down"); + } + } + } + + private void stopTask() { + if(future != null) { + future.cancel(false); + future=null; + } + } + + + /** + * Removes all elements from suspected_mbrs that are not in the new membership + */ + public void adjustSuspectedMembers(Vector
      new_mbrship) { + if(new_mbrship == null || new_mbrship.isEmpty()) return; + synchronized(suspected_mbrs) { + boolean modified=suspected_mbrs.retainAll(new_mbrship); + if(log.isTraceEnabled() && modified) + log.trace("adjusted suspected_mbrs: " + suspected_mbrs); + if(suspected_mbrs.isEmpty()) + stopTask(); + } + } + + + public void run() { + Message suspect_msg; + FdHeader hdr; + + if(log.isDebugEnabled()) + log.debug("broadcasting SUSPECT message (suspected_mbrs=" + suspected_mbrs + ") to group"); + + synchronized(suspected_mbrs) { + if(suspected_mbrs.isEmpty()) { + stopTask(); + if(log.isDebugEnabled()) log.debug("task done (no suspected members)"); + return; + } + + hdr=new FdHeader(FdHeader.SUSPECT); + hdr.mbrs=new HashSet
      (suspected_mbrs); + } + suspect_msg=new Message(); // mcast SUSPECT to all members + suspect_msg.setFlag(Message.OOB); + suspect_msg.putHeader(name, hdr); + down_prot.down(new Event(Event.MSG, suspect_msg)); + if(log.isDebugEnabled()) log.debug("task done"); + } + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/FILE_PING.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/FILE_PING.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/FILE_PING.java 17 Aug 2012 14:51:05 -0000 1.1 @@ -0,0 +1,177 @@ +package org.jgroups.protocols; + +import org.jgroups.Address; +import org.jgroups.Event; +import org.jgroups.Message; +import org.jgroups.util.Util; +import org.jgroups.util.Promise; + +import java.io.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + + +/** + * Simple discovery protocol which uses a file on shared storage such as an SMB share, NFS mount or S3. The local + * address information, e.g. UUID and physical addresses mappings are written to the file and the content is read and + * added to our transport's UUID-PhysicalAddress cache.

      + * The design is at doc/design/FILE_PING.txt + * @author Bela Ban + * @version $Id: FILE_PING.java,v 1.1 2012/08/17 14:51:05 marcin Exp $ + */ +public class FILE_PING extends Discovery { + private static final String name="FILE_PING"; + private static final String SUFFIX=".node"; + + /* ----------------------------------------- Properties -------------------------------------------------- */ + + + // location of the shared directory used for discovery" + private String location=File.separator + "tmp" + File.separator + "jgroups"; + + + /* --------------------------------------------- Fields ------------------------------------------------------ */ + protected File root_dir=null; + protected FilenameFilter filter; + + + public String getName() { + return name; + } + + + public boolean setProperties(Properties props) { + String str; + + str=props.getProperty("location"); + if(str != null) { + location=str; + props.remove("location"); + } + + return super.setProperties(props); + } + + + + public void init() throws Exception { + super.init(); + root_dir=new File(location); + if(root_dir.exists()) { + if(!root_dir.isDirectory()) + throw new IllegalArgumentException("location " + root_dir.getPath() + " is not a directory"); + } + else { + root_dir.mkdirs(); + } + if(!root_dir.exists()) + throw new IllegalArgumentException("location " + root_dir.getPath() + " could not be accessed"); + + filter=new FilenameFilter() { + public boolean accept(File dir, String name) { + return name.endsWith(SUFFIX); + } + }; + } + + + public void sendGetMembersRequest(Promise promise) throws Exception{ + String cluster_name=group_addr; + List

      existing_mbrs=readAll(cluster_name); + + // If we don't find any files, return immediately + if(existing_mbrs.isEmpty() || (existing_mbrs.size() == 1 && existing_mbrs.contains(local_addr))) { + if(promise != null) { + promise.setResult(null); + } + } + else { + + // 1. Send GET_MBRS_REQ message to members listed in the file + for(final Address dest: existing_mbrs) { + if(dest.equals(local_addr)) + continue; + PingHeader hdr=new PingHeader(PingHeader.GET_MBRS_REQ, null); + final Message msg=new Message(dest); + msg.setFlag(Message.OOB); + msg.putHeader(getName(), hdr); // needs to be getName(), so we might get "MPING" ! + if(log.isTraceEnabled()) + log.trace("[FIND_INITIAL_MBRS] sending PING request to " + msg.getDest()); + timer.submit(new Runnable() { + public void run() { + try { + down_prot.down(new Event(Event.MSG, msg)); + } + catch(Exception ex){ + if(log.isErrorEnabled()) + log.error("failed sending discovery request to " + dest, ex); + } + } + }); + } + } + + // Write my own data to file + writeToFile(local_addr, cluster_name); + } + + + + /** + * Reads all information from the given directory under clustername + * @return + */ + private List
      readAll(String clustername) { + List
      retval=new ArrayList
      (); + File dir=new File(root_dir, clustername); + if(!dir.exists()) + dir.mkdir(); + + File[] files=dir.listFiles(filter); + if(files != null) { + for(File file: files) + retval.add(readFile(file)); + } + return retval; + } + + private static Address readFile(File file) { + Address retval=null; + DataInputStream in=null; + + try { + in=new DataInputStream(new FileInputStream(file)); + return Util.readAddress(in); + } + catch(Exception e) { + } + finally { + Util.close(in); + } + return retval; + } + + private void writeToFile(Address addr, String clustername) { + DataOutputStream out=null; + File dir=new File(root_dir, clustername); + if(!dir.exists()) + dir.mkdir(); + + File file=new File(dir, addr.toString() + SUFFIX); + file.deleteOnExit(); + + try { + out=new DataOutputStream(new FileOutputStream(file)); + Util.writeAddress(addr, out); + } + catch(Exception e) { + } + finally { + Util.close(out); + } + } + + + +} \ No newline at end of file Index: 3rdParty_sources/jgroups/org/jgroups/protocols/FRAG.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/FRAG.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/FRAG.java 17 Aug 2012 14:51:06 -0000 1.1 @@ -0,0 +1,579 @@ + +package org.jgroups.protocols; + +import org.jgroups.Address; +import org.jgroups.Event; +import org.jgroups.Message; +import org.jgroups.View; +import org.jgroups.stack.Protocol; +import org.jgroups.util.ExposedByteArrayOutputStream; +import org.jgroups.util.Util; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.util.*; + + + +/** + * Fragmentation layer. Fragments messages larger than FRAG_SIZE into smaller packets. + * Reassembles fragmented packets into bigger ones. The fragmentation number is prepended + * to the messages as a header (and removed at the receiving side).

      + * Each fragment is identified by (a) the sender (part of the message to which the header is appended), + * (b) the fragmentation ID (which is unique per FRAG layer (monotonically increasing) and (c) the + * fragement ID which ranges from 0 to number_of_fragments-1.

      + * Requirement: lossless delivery (e.g. NAK, ACK). No requirement on ordering. Works for both unicast and + * multicast messages. + * @author Bela Ban + * @author Filip Hanik + * @version $Id: FRAG.java,v 1.1 2012/08/17 14:51:06 marcin Exp $ + */ +public class FRAG extends Protocol { + private int frag_size=8192; // conservative value + + private int max_retained_buffer=70000; + + /*the fragmentation list contains a fragmentation table per sender + *this way it becomes easier to clean up if a sender (member) leaves or crashes + */ + private final FragmentationList fragment_list=new FragmentationList(); + private int curr_id=1; + private final ExposedByteArrayOutputStream bos=new ExposedByteArrayOutputStream(1024); + private final Vector

      members=new Vector
      (11); + private final static String name="FRAG"; + + long num_sent_msgs=0; + long num_sent_frags=0; + long num_received_msgs=0; + long num_received_frags=0; + + + public String getName() { + return name; + } + + public int getFragSize() {return frag_size;} + public void setFragSize(int s) {frag_size=s;} + public long getNumberOfSentMessages() {return num_sent_msgs;} + public long getNumberOfSentFragments() {return num_sent_frags;} + public long getNumberOfReceivedMessages() {return num_received_msgs;} + public long getNumberOfReceivedFragments() {return num_received_frags;} + + /** + * Setup the Protocol instance acording to the configuration string + */ + public boolean setProperties(Properties props) { + String str; + + super.setProperties(props); + str=props.getProperty("frag_size"); + if(str != null) { + frag_size=Integer.parseInt(str); + props.remove("frag_size"); + } + + str=props.getProperty("max_retained_buffer"); + if(str != null) { + max_retained_buffer=Integer.parseInt(str); + props.remove("max_retained_buffer"); + } + + if(!props.isEmpty()) { + log.error("the following properties are not recognized: " + props); + return false; + } + return true; + } + + public void init() throws Exception { + super.init(); + Map info=new HashMap(1); + info.put("frag_size", frag_size); + up_prot.up(new Event(Event.INFO, info)); + down_prot.down(new Event(Event.INFO, info)); + } + + public void resetStats() { + super.resetStats(); + num_sent_msgs=num_sent_frags=num_received_msgs=num_received_frags=0; + } + + + /** + * Fragment a packet if larger than frag_size (add a header). Otherwise just pass down. Only + * add a header if framentation is needed ! + */ + public Object down(Event evt) { + switch(evt.getType()) { + + case Event.MSG: + Message msg=(Message)evt.getArg(); + long size=msg.size(); + num_sent_msgs++; + if(size > frag_size) { + if(log.isTraceEnabled()) { + StringBuilder sb=new StringBuilder("message size is "); + sb.append(size).append(", will fragment (frag_size=").append(frag_size).append(')'); + log.trace(sb.toString()); + } + fragment(msg); // Fragment and pass down + return null; + } + break; + + case Event.VIEW_CHANGE: + handleViewChange((View)evt.getArg()); + break; + + case Event.CONFIG: + Object ret=down_prot.down(evt); + if(log.isDebugEnabled()) log.debug("received CONFIG event: " + evt.getArg()); + handleConfigEvent((Map)evt.getArg()); + return ret; + } + + return down_prot.down(evt); // Pass on to the layer below us + } + + + /** + * If event is a message, if it is fragmented, re-assemble fragments into big message and pass up the stack. + */ + public Object up(Event evt) { + switch(evt.getType()) { + + case Event.MSG: + Message msg=(Message)evt.getArg(); + FragHeader hdr=(FragHeader)msg.getHeader(name); + if(hdr != null) { // needs to be defragmented + unfragment(msg, hdr); // Unfragment and possibly pass up + return null; + } + else { + num_received_msgs++; + } + break; + + case Event.VIEW_CHANGE: + handleViewChange((View)evt.getArg()); + break; + + case Event.CONFIG: + Object ret=up_prot.up(evt); + if(log.isDebugEnabled()) log.debug("received CONFIG event: " + evt.getArg()); + handleConfigEvent((Map)evt.getArg()); + return ret; + } + + return up_prot.up(evt); // Pass up to the layer above us by default + } + + private void handleViewChange(View view) { + Vector new_mbrs=view.getMembers(), left_mbrs; + Address mbr; + + left_mbrs=Util.determineLeftMembers(members, new_mbrs); + members.clear(); + members.addAll(new_mbrs); + + for(int i=0; i < left_mbrs.size(); i++) { + mbr=(Address)left_mbrs.elementAt(i); + //the new view doesn't contain the sender, he must have left, + //hence we will clear all his fragmentation tables + fragment_list.remove(mbr); + if(log.isTraceEnabled()) + log.trace("[VIEW_CHANGE] removed " + mbr + " from fragmentation table"); + } + } + + + /** + * Send all fragments as separate messages (with same ID !). + * Example: + *
      +     * Given the generated ID is 2344, number of fragments=3, message {dst,src,buf}
      +     * would be fragmented into:
      +     * 

      + * [2344,3,0]{dst,src,buf1}, + * [2344,3,1]{dst,src,buf2} and + * [2344,3,2]{dst,src,buf3} + *

      + */ + private void fragment(Message msg) { + DataOutputStream out=null; + byte[] buffer; + byte[] fragments[]; + Event evt; + FragHeader hdr; + Message frag_msg; + Address dest=msg.getDest(), src=msg.getSrc(); + long id=curr_id++; // used as seqnos + int num_frags; + + try { + // Write message into a byte buffer and fragment it + // Synchronization around bos is needed for concurrent access (http://jira.jboss.com/jira/browse/JGRP-215) + synchronized(bos) { + bos.reset(this.max_retained_buffer); + out=new DataOutputStream(bos); + msg.writeTo(out); + out.flush(); + buffer=bos.getRawBuffer(); + fragments=Util.fragmentBuffer(buffer, frag_size, bos.size()); + } + + num_frags=fragments.length; + num_sent_frags+=num_frags; + + if(log.isTraceEnabled()) { + StringBuilder sb=new StringBuilder(); + sb.append("fragmenting packet to ").append(dest != null ? dest.toString() : ""); + sb.append(" (size=").append(buffer.length).append(") into ").append(num_frags); + sb.append(" fragment(s) [frag_size=").append(frag_size).append(']'); + log.trace(sb.toString()); + } + + for(int i=0; i < num_frags; i++) { + frag_msg=new Message(dest, src, fragments[i]); + hdr=new FragHeader(id, i, num_frags); + frag_msg.putHeader(name, hdr); + evt=new Event(Event.MSG, frag_msg); + down_prot.down(evt); + } + } + catch(Exception e) { + log.error("exception occurred trying to fragment message", e); + } + finally { + Util.close(out); + } + } + + + /** + * 1. Get all the fragment buffers + * 2. When all are received -> Assemble them into one big buffer + * 3. Read headers and byte buffer from big buffer + * 4. Set headers and buffer in msg + * 5. Pass msg up the stack + */ + private void unfragment(Message msg, FragHeader hdr) { + FragmentationTable frag_table; + Address sender=msg.getSrc(); + Message assembled_msg; + byte[] m; + ByteArrayInputStream bis; + DataInputStream in=null; + + frag_table=fragment_list.get(sender); + if(frag_table == null) { + frag_table=new FragmentationTable(sender); + try { + fragment_list.add(sender, frag_table); + } + catch(IllegalArgumentException x) { // the entry has already been added, probably in parallel from another thread + frag_table=fragment_list.get(sender); + } + } + num_received_frags++; + m=frag_table.add(hdr.id, hdr.frag_id, hdr.num_frags, msg.getBuffer()); + if(m != null) { + try { + bis=new ByteArrayInputStream(m); + in=new DataInputStream(bis); + assembled_msg=new Message(false); + assembled_msg.readFrom(in); + if(log.isTraceEnabled()) log.trace("assembled_msg is " + assembled_msg); + assembled_msg.setSrc(sender); // needed ? YES, because fragments have a null src !! + num_received_msgs++; + up_prot.up(new Event(Event.MSG, assembled_msg)); + } + catch(Exception e) { + log.error("failed unfragmenting a message", e); + } + finally { + Util.close(in); + } + } + } + + + void handleConfigEvent(Map map) { + if(map == null) return; + if(map.containsKey("frag_size")) { + frag_size=((Integer)map.get("frag_size")).intValue(); + if(log.isDebugEnabled()) log.debug("setting frag_size=" + frag_size); + } + } + + + + + /** + * A fragmentation list keeps a list of fragmentation tables + * sorted by an Address ( the sender ). + * This way, if the sender disappears or leaves the group half way + * sending the content, we can simply remove this members fragmentation + * table and clean up the memory of the receiver. + * We do not have to do the same for the sender, since the sender doesn't keep a fragmentation table + */ + static class FragmentationList { + /* initialize the hashtable to hold all the fragmentation tables + * 11 is the best growth capacity to start with
      + * HashMap + */ + private final HashMap frag_tables=new HashMap(11); + + + /** + * Adds a fragmentation table for this particular sender + * If this sender already has a fragmentation table, an IllegalArgumentException + * will be thrown. + * @param sender - the address of the sender, cannot be null + * @param table - the fragmentation table of this sender, cannot be null + * @throws IllegalArgumentException if an entry for this sender already exist + */ + public void add(Address sender, FragmentationTable table) throws IllegalArgumentException { + FragmentationTable healthCheck; + + synchronized(frag_tables) { + healthCheck=(FragmentationTable)frag_tables.get(sender); + if(healthCheck == null) { + frag_tables.put(sender, table); + } + else { + throw new IllegalArgumentException("Sender <" + sender + "> already exists in the fragementation list"); + } + } + } + + /** + * returns a fragmentation table for this sender + * returns null if the sender doesn't have a fragmentation table + * @return the fragmentation table for this sender, or null if no table exist + */ + public FragmentationTable get(Address sender) { + synchronized(frag_tables) { + return (FragmentationTable)frag_tables.get(sender); + } + } + + + /** + * returns true if this sender already holds a + * fragmentation for this sender, false otherwise + * @param sender - the sender, cannot be null + * @return true if this sender already has a fragmentation table + */ + public boolean containsSender(Address sender) { + synchronized(frag_tables) { + return frag_tables.containsKey(sender); + } + } + + /** + * removes the fragmentation table from the list. + * after this operation, the fragementation list will no longer + * hold a reference to this sender's fragmentation table + * @param sender - the sender who's fragmentation table you wish to remove, cannot be null + * @return true if the table was removed, false if the sender doesn't have an entry + */ + public boolean remove(Address sender) { + synchronized(frag_tables) { + boolean result=containsSender(sender); + frag_tables.remove(sender); + return result; + } + } + + /** + * returns a list of all the senders that have fragmentation tables opened. + * @return an array of all the senders in the fragmentation list + */ + public Address[] getSenders() { + Address[] result; + int index=0; + + synchronized(frag_tables) { + result=new Address[frag_tables.size()]; + for(Iterator it=frag_tables.keySet().iterator(); it.hasNext();) { + result[index++]=(Address)it.next(); + } + } + return result; + } + + public String toString() { + Map.Entry entry; + StringBuilder buf=new StringBuilder("Fragmentation list contains "); + synchronized(frag_tables) { + buf.append(frag_tables.size()).append(" tables\n"); + for(Iterator it=frag_tables.entrySet().iterator(); it.hasNext();) { + entry=(Map.Entry)it.next(); + buf.append(entry.getKey()).append(": " ).append(entry.getValue()).append("\n"); + } + } + return buf.toString(); + } + + } + + /** + * Keeps track of the fragments that are received. + * Reassembles fragements into entire messages when all fragments have been received. + * The fragmentation holds a an array of byte arrays for a unique sender + * The first dimension of the array is the order of the fragmentation, in case the arrive out of order + */ + static class FragmentationTable { + private final Address sender; + /* the hashtable that holds the fragmentation entries for this sender*/ + private final Hashtable h=new Hashtable(11); // keys: frag_ids, vals: Entrys + + + FragmentationTable(Address sender) { + this.sender=sender; + } + + + /** + * inner class represents an entry for a message + * each entry holds an array of byte arrays sorted + * once all the byte buffer entries have been filled + * the fragmentation is considered complete. + */ + static class Entry { + //the total number of fragment in this message + int tot_frags=0; + // each fragment is a byte buffer + byte[] fragments[]=null; + //the number of fragments we have received + int number_of_frags_recvd=0; + // the message ID + long msg_id=-1; + + /** + * Creates a new entry + * + * @param tot_frags the number of fragments to expect for this message + */ + Entry(long msg_id, int tot_frags) { + this.msg_id=msg_id; + this.tot_frags=tot_frags; + fragments=new byte[tot_frags][]; + for(int i=0; i < tot_frags; i++) { + fragments[i]=null; + } + } + + /** + * adds on fragmentation buffer to the message + * + * @param frag_id the number of the fragment being added 0..(tot_num_of_frags - 1) + * @param frag the byte buffer containing the data for this fragmentation, should not be null + */ + public void set(int frag_id, byte[] frag) { + fragments[frag_id]=frag; + number_of_frags_recvd++; + } + + /** + * returns true if this fragmentation is complete + * ie, all fragmentations have been received for this buffer + */ + public boolean isComplete() { + /*first make the simple check*/ + if(number_of_frags_recvd < tot_frags) { + return false; + } + /*then double check just in case*/ + for(int i=0; i < fragments.length; i++) { + if(fragments[i] == null) + return false; + } + /*all fragmentations have been received*/ + return true; + } + + /** + * Assembles all the fragmentations into one buffer + * this method does not check if the fragmentation is complete + * + * @return the complete message in one buffer + */ + public byte[] assembleBuffer() { + return Util.defragmentBuffer(fragments); + } + + /** + * debug only + */ + public String toString() { + StringBuilder ret=new StringBuilder(); + ret.append("[tot_frags=").append(tot_frags).append(", number_of_frags_recvd=").append(number_of_frags_recvd).append(']'); + return ret.toString(); + } + + public int hashCode() { + return super.hashCode(); + } + } + + + /** + * Creates a new entry if not yet present. Adds the fragment. + * If all fragements for a given message have been received, + * an entire message is reassembled and returned. + * Otherwise null is returned. + * + * @param id - the message ID, unique for a sender + * @param frag_id the index of this fragmentation (0..tot_frags-1) + * @param tot_frags the total number of fragmentations expected + * @param fragment - the byte buffer for this fragment + */ + public synchronized byte[] add(long id, int frag_id, int tot_frags, byte[] fragment) { + + /*initialize the return value to default not complete */ + byte[] retval=null; + + Entry e=(Entry)h.get(new Long(id)); + + if(e == null) { // Create new entry if not yet present + e=new Entry(id, tot_frags); + h.put(new Long(id), e); + } + + e.set(frag_id, fragment); + if(e.isComplete()) { + retval=e.assembleBuffer(); + h.remove(new Long(id)); + } + + return retval; + } + + public void reset() { + } + + public String toString() { + StringBuilder buf=new StringBuilder("Fragmentation Table Sender:").append(sender).append("\n\t"); + java.util.Enumeration e=this.h.elements(); + while(e.hasMoreElements()) { + Entry entry=(Entry)e.nextElement(); + int count=0; + for(int i=0; i < entry.fragments.length; i++) { + if(entry.fragments[i] != null) { + count++; + } + } + buf.append("Message ID:").append(entry.msg_id).append("\n\t"); + buf.append("Total Frags:").append(entry.tot_frags).append("\n\t"); + buf.append("Frags Received:").append(count).append("\n\n"); + } + return buf.toString(); + } + } + +} + + Index: 3rdParty_sources/jgroups/org/jgroups/protocols/FRAG2.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/FRAG2.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/FRAG2.java 17 Aug 2012 14:51:05 -0000 1.1 @@ -0,0 +1,604 @@ +package org.jgroups.protocols; + +import org.jgroups.Address; +import org.jgroups.Event; +import org.jgroups.Message; +import org.jgroups.View; +import org.jgroups.stack.Protocol; +import org.jgroups.util.Range; +import org.jgroups.util.Util; + +import java.util.*; +import java.util.concurrent.atomic.AtomicLong; + + +/** + * Fragmentation layer. Fragments messages larger than frag_size into smaller packets. + * Reassembles fragmented packets into bigger ones. The fragmentation number is prepended + * to the messages as a header (and removed at the receiving side).

      + * Each fragment is identified by (a) the sender (part of the message to which the header is appended), + * (b) the fragmentation ID (which is unique per FRAG2 layer (monotonically increasing) and (c) the + * fragement ID which ranges from 0 to number_of_fragments-1.

      + * Requirement: lossless delivery (e.g. NAK, ACK). No requirement on ordering. Works for both unicast and + * multicast messages.
      + * Compared to FRAG, this protocol does not need to serialize the message in order to break it into + * smaller fragments: it looks only at the message's buffer, which is a byte[] array anyway. We assume that the + * size addition for headers and src and dest address is minimal when the transport finally has to serialize the + * message, so we add a constant (200 bytes). + * @author Bela Ban + * @version $Id: FRAG2.java,v 1.1 2012/08/17 14:51:05 marcin Exp $ + */ +public class FRAG2 extends Protocol { + + /** The max number of bytes in a message. If a message's buffer is bigger, it will be fragmented */ + int frag_size=1500; + + /*the fragmentation list contains a fragmentation table per sender + *this way it becomes easier to clean up if a sender (member) leaves or crashes + */ + private final FragmentationList fragment_list=new FragmentationList(); + private int curr_id=1; + private final Vector members=new Vector(11); + private static final String name="FRAG2"; + + AtomicLong num_sent_msgs=new AtomicLong(0); + AtomicLong num_received_msgs=new AtomicLong(0); + AtomicLong num_sent_frags=new AtomicLong(0); + AtomicLong num_received_frags=new AtomicLong(0); + + + public final String getName() { + return name; + } + + public int getFragSize() {return frag_size;} + public void setFragSize(int s) {frag_size=s;} + /** @deprecated overhead was removed in 2.6.10 */ + public int getOverhead() {return 0;} + /** @deprecated overhead was removed in 2.6.10 */ + public void setOverhead(int o) {} + public long getNumberOfSentMessages() {return num_sent_msgs.get();} + public long getNumberOfSentFragments() {return num_sent_frags.get();} + public long getNumberOfReceivedMessages() {return num_received_msgs.get();} + public long getNumberOfReceivedFragments() {return num_received_frags.get();} + + + synchronized int getNextId() { + return curr_id++; + } + + /** Setup the Protocol instance acording to the configuration string */ + public boolean setProperties(Properties props) { + String str; + + super.setProperties(props); + str=props.getProperty("frag_size"); + if(str != null) { + frag_size=Integer.parseInt(str); + props.remove("frag_size"); + } + + str=props.getProperty("overhead"); + if(str != null) { + props.remove("overhead"); + log.warn("overhead is ignored (was removed in 2.6.10)"); + } + + int old_frag_size=frag_size; + if(frag_size <=0) { + log.error("frag_size=" + old_frag_size + ", new frag_size=" + frag_size + ": new frag_size is invalid"); + return false; + } + + if(log.isDebugEnabled()) + log.debug("frag_size=" + old_frag_size + ", new frag_size=" + frag_size); + + if(!props.isEmpty()) { + log.error("FRAG2.setProperties(): the following properties are not recognized: " + props); + return false; + } + return true; + } + + public void init() throws Exception { + super.init(); + Map info=new HashMap(1); + info.put("frag_size", frag_size); + up_prot.up(new Event(Event.INFO, info)); + down_prot.down(new Event(Event.INFO, info)); + } + + + public void resetStats() { + super.resetStats(); + num_sent_msgs.set(0); + num_sent_frags.set(0); + num_received_frags.set(0); + num_received_msgs.set(0); + } + + + + /** + * Fragment a packet if larger than frag_size (add a header). Otherwise just pass down. Only + * add a header if framentation is needed ! + */ + public Object down(Event evt) { + switch(evt.getType()) { + + case Event.MSG: + Message msg=(Message)evt.getArg(); + long size=msg.getLength(); + num_sent_msgs.incrementAndGet(); + if(size > frag_size) { + if(log.isTraceEnabled()) { + log.trace(new StringBuilder("message's buffer size is ").append(size) + .append(", will fragment ").append("(frag_size=").append(frag_size).append(')')); + } + fragment(msg); // Fragment and pass down + return null; + } + break; + + case Event.VIEW_CHANGE: + handleView((View)evt.getArg()); + break; + + case Event.CONFIG: + Object ret=down_prot.down(evt); + if(log.isDebugEnabled()) log.debug("received CONFIG event: " + evt.getArg()); + handleConfigEvent((Map)evt.getArg()); + return ret; + } + + return down_prot.down(evt); // Pass on to the layer below us + } + + + /** + * If event is a message, if it is fragmented, re-assemble fragments into big message and pass up + * the stack. + */ + public Object up(Event evt) { + switch(evt.getType()) { + + case Event.MSG: + Message msg=(Message)evt.getArg(); + FragHeader hdr=(FragHeader)msg.getHeader(name); + if(hdr != null) { // needs to be defragmented + unfragment(msg, hdr); // Unfragment and possibly pass up + return null; + } + else { + num_received_msgs.incrementAndGet(); + } + break; + + case Event.VIEW_CHANGE: + handleView((View)evt.getArg()); + break; + + case Event.CONFIG: + Object ret=up_prot.up(evt); + if(log.isDebugEnabled()) log.debug("received CONFIG event: " + evt.getArg()); + handleConfigEvent((Map)evt.getArg()); + return ret; + } + + return up_prot.up(evt); // Pass up to the layer above us by default + } + + + private void handleView(View view) { + //don't do anything if this dude is sending out the view change + //we are receiving a view change, + //in here we check for the + Vector new_mbrs=view.getMembers(), left_mbrs; + Address mbr; + + left_mbrs=Util.determineLeftMembers(members, new_mbrs); + members.clear(); + members.addAll(new_mbrs); + + for(int i=0; i < left_mbrs.size(); i++) { + mbr=(Address)left_mbrs.elementAt(i); + //the new view doesn't contain the sender, he must have left, + //hence we will clear all his fragmentation tables + fragment_list.remove(mbr); + if(log.isTraceEnabled()) log.trace("[VIEW_CHANGE] removed " + mbr + " from fragmentation table"); + } + } + + + /** Send all fragments as separate messages (with same ID !). + Example: +

      +     Given the generated ID is 2344, number of fragments=3, message {dst,src,buf}
      +     would be fragmented into:
      +
      +     [2344,3,0]{dst,src,buf1},
      +     [2344,3,1]{dst,src,buf2} and
      +     [2344,3,2]{dst,src,buf3}
      +     
      + */ + void fragment(Message msg) { + byte[] buffer; + List fragments; + Event evt; + FragHeader hdr; + Message frag_msg; + Address dest=msg.getDest(); + long id=getNextId(); // used as seqnos + int num_frags; + Range r; + + try { + buffer=msg.getBuffer(); + fragments=Util.computeFragOffsets(buffer, frag_size); + num_frags=fragments.size(); + num_sent_frags.addAndGet(num_frags); + + if(log.isTraceEnabled()) { + StringBuilder sb=new StringBuilder("fragmenting packet to "); + sb.append((dest != null ? dest.toString() : "")).append(" (size=").append(buffer.length); + sb.append(") into ").append(num_frags).append(" fragment(s) [frag_size=").append(frag_size).append(']'); + log.trace(sb.toString()); + } + + for(int i=0; i < fragments.size(); i++) { + r=(Range)fragments.get(i); + // Copy the original msg (needed because we need to copy the headers too) + frag_msg=msg.copy(false); // don't copy the buffer, only src, dest and headers + frag_msg.setBuffer(buffer, (int)r.low, (int)r.high); + hdr=new FragHeader(id, i, num_frags); + frag_msg.putHeader(name, hdr); + evt=new Event(Event.MSG, frag_msg); + down_prot.down(evt); + } + } + catch(Exception e) { + if(log.isErrorEnabled()) log.error("fragmentation failure", e); + } + } + + + /** + 1. Get all the fragment buffers + 2. When all are received -> Assemble them into one big buffer + 3. Read headers and byte buffer from big buffer + 4. Set headers and buffer in msg + 5. Pass msg up the stack + */ + private void unfragment(Message msg, FragHeader hdr) { + FragmentationTable frag_table; + Address sender=msg.getSrc(); + Message assembled_msg; + + frag_table=fragment_list.get(sender); + if(frag_table == null) { + frag_table=new FragmentationTable(sender); + try { + fragment_list.add(sender, frag_table); + } + catch(IllegalArgumentException x) { // the entry has already been added, probably in parallel from another thread + frag_table=fragment_list.get(sender); + } + } + num_received_frags.incrementAndGet(); + assembled_msg=frag_table.add(hdr.id, hdr.frag_id, hdr.num_frags, msg); + if(assembled_msg != null) { + try { + if(log.isTraceEnabled()) log.trace("assembled_msg is " + assembled_msg); + assembled_msg.setSrc(sender); // needed ? YES, because fragments have a null src !! + num_received_msgs.incrementAndGet(); + up_prot.up(new Event(Event.MSG, assembled_msg)); + } + catch(Exception e) { + if(log.isErrorEnabled()) log.error("unfragmentation failed", e); + } + } + } + + + void handleConfigEvent(Map map) { + if(map == null) return; + if(map.containsKey("frag_size")) { + frag_size=((Integer)map.get("frag_size")).intValue(); + if(log.isDebugEnabled()) log.debug("setting frag_size=" + frag_size); + } + } + + + + + /** + * A fragmentation list keeps a list of fragmentation tables + * sorted by an Address ( the sender ). + * This way, if the sender disappears or leaves the group half way + * sending the content, we can simply remove this members fragmentation + * table and clean up the memory of the receiver. + * We do not have to do the same for the sender, since the sender doesn't keep a fragmentation table + */ + static class FragmentationList { + /* * HashMap, initialize the hashtable to hold all the fragmentation + * tables (11 is the best growth capacity to start with) + */ + private final HashMap frag_tables=new HashMap(11); + + + /** + * Adds a fragmentation table for this particular sender + * If this sender already has a fragmentation table, an IllegalArgumentException + * will be thrown. + * @param sender - the address of the sender, cannot be null + * @param table - the fragmentation table of this sender, cannot be null + * @exception IllegalArgumentException if an entry for this sender already exist + */ + public void add(Address sender, FragmentationTable table) throws IllegalArgumentException { + FragmentationTable healthCheck; + + synchronized(frag_tables) { + healthCheck=(FragmentationTable)frag_tables.get(sender); + if(healthCheck == null) { + frag_tables.put(sender, table); + } + else { + throw new IllegalArgumentException("Sender <" + sender + "> already exists in the fragementation list."); + } + } + } + + /** + * returns a fragmentation table for this sender + * returns null if the sender doesn't have a fragmentation table + * @return the fragmentation table for this sender, or null if no table exist + */ + public FragmentationTable get(Address sender) { + synchronized(frag_tables) { + return (FragmentationTable)frag_tables.get(sender); + } + } + + + /** + * returns true if this sender already holds a + * fragmentation for this sender, false otherwise + * @param sender - the sender, cannot be null + * @return true if this sender already has a fragmentation table + */ + public boolean containsSender(Address sender) { + synchronized(frag_tables) { + return frag_tables.containsKey(sender); + } + } + + /** + * removes the fragmentation table from the list. + * after this operation, the fragementation list will no longer + * hold a reference to this sender's fragmentation table + * @param sender - the sender who's fragmentation table you wish to remove, cannot be null + * @return true if the table was removed, false if the sender doesn't have an entry + */ + public boolean remove(Address sender) { + synchronized(frag_tables) { + boolean result=containsSender(sender); + frag_tables.remove(sender); + return result; + } + } + + /** + * returns a list of all the senders that have fragmentation tables + * opened. + * @return an array of all the senders in the fragmentation list + */ + public Address[] getSenders() { + Address[] result; + int index=0; + + synchronized(frag_tables) { + result=new Address[frag_tables.size()]; + for(Iterator it=frag_tables.keySet().iterator(); it.hasNext();) { + result[index++]=(Address)it.next(); + } + } + return result; + } + + public String toString() { + Map.Entry entry; + StringBuilder buf=new StringBuilder("Fragmentation list contains "); + synchronized(frag_tables) { + buf.append(frag_tables.size()).append(" tables\n"); + for(Iterator it=frag_tables.entrySet().iterator(); it.hasNext();) { + entry=(Map.Entry)it.next(); + buf.append(entry.getKey()).append(": " ).append(entry.getValue()).append("\n"); + } + } + return buf.toString(); + } + + } + + /** + * Keeps track of the fragments that are received. + * Reassembles fragements into entire messages when all fragments have been received. + * The fragmentation holds a an array of byte arrays for a unique sender + * The first dimension of the array is the order of the fragmentation, in case the arrive out of order + */ + static class FragmentationTable { + private final Address sender; + /* the hashtable that holds the fragmentation entries for this sender*/ + private final Hashtable h=new Hashtable(11); // keys: frag_ids, vals: Entrys + + + FragmentationTable(Address sender) { + this.sender=sender; + } + + + /** + * inner class represents an entry for a message + * each entry holds an array of byte arrays sorted + * once all the byte buffer entries have been filled + * the fragmentation is considered complete. + */ + static class Entry { + //the total number of fragment in this message + int tot_frags=0; + // each fragment is a byte buffer + Message fragments[]=null; + //the number of fragments we have received + int number_of_frags_recvd=0; + // the message ID + long msg_id=-1; + + /** + * Creates a new entry + * @param tot_frags the number of fragments to expect for this message + */ + Entry(long msg_id, int tot_frags) { + this.msg_id=msg_id; + this.tot_frags=tot_frags; + fragments=new Message[tot_frags]; + for(int i=0; i < tot_frags; i++) + fragments[i]=null; + } + + /** + * adds on fragmentation buffer to the message + * @param frag_id the number of the fragment being added 0..(tot_num_of_frags - 1) + * @param frag the byte buffer containing the data for this fragmentation, should not be null + */ + public void set(int frag_id, Message frag) { + // don't count an already received fragment (should not happen though because the + // reliable transmission protocol(s) below should weed out duplicates + if(fragments[frag_id] == null) { + fragments[frag_id]=frag; + number_of_frags_recvd++; + } + } + + /** returns true if this fragmentation is complete + * ie, all fragmentations have been received for this buffer + * + */ + public boolean isComplete() { + /*first make the simple check*/ + if(number_of_frags_recvd < tot_frags) { + return false; + } + /*then double check just in case*/ + for(int i=0; i < fragments.length; i++) { + if(fragments[i] == null) + return false; + } + /*all fragmentations have been received*/ + return true; + } + + /** + * Assembles all the fragments into one buffer. Takes all Messages, and combines their buffers into one + * buffer. + * This method does not check if the fragmentation is complete (use {@link #isComplete()} to verify + * before calling this method) + * @return the complete message in one buffer + * + */ + public Message assembleMessage() { + Message retval; + byte[] combined_buffer, tmp; + int combined_length=0, length, offset; + Message fragment; + int index=0; + + for(int i=0; i < fragments.length; i++) { + fragment=fragments[i]; + combined_length+=fragment.getLength(); + } + + combined_buffer=new byte[combined_length]; + for(int i=0; i < fragments.length; i++) { + fragment=fragments[i]; + tmp=fragment.getRawBuffer(); + length=fragment.getLength(); + offset=fragment.getOffset(); + System.arraycopy(tmp, offset, combined_buffer, index, length); + index+=length; + } + + retval=fragments[0].copy(false); + retval.setBuffer(combined_buffer); + return retval; + } + + /** + * debug only + */ + public String toString() { + StringBuilder ret=new StringBuilder(); + ret.append("[tot_frags=").append(tot_frags).append(", number_of_frags_recvd=").append(number_of_frags_recvd).append(']'); + return ret.toString(); + } + + public int hashCode() { + return super.hashCode(); + } + } + + + /** + * Creates a new entry if not yet present. Adds the fragment. + * If all fragements for a given message have been received, + * an entire message is reassembled and returned. + * Otherwise null is returned. + * @param id - the message ID, unique for a sender + * @param frag_id the index of this fragmentation (0..tot_frags-1) + * @param tot_frags the total number of fragmentations expected + * @param fragment - the byte buffer for this fragment + */ + public synchronized Message add(long id, int frag_id, int tot_frags, Message fragment) { + Message retval=null; + + Entry e=(Entry)h.get(new Long(id)); + + if(e == null) { // Create new entry if not yet present + e=new Entry(id, tot_frags); + h.put(new Long(id), e); + } + + e.set(frag_id, fragment); + if(e.isComplete()) { + retval=e.assembleMessage(); + h.remove(new Long(id)); + } + + return retval; + } + + + public void reset() { + } + + public String toString() { + StringBuilder buf=new StringBuilder("Fragmentation Table Sender:").append(sender).append("\n\t"); + java.util.Enumeration e=this.h.elements(); + while(e.hasMoreElements()) { + Entry entry=(Entry)e.nextElement(); + int count=0; + for(int i=0; i < entry.fragments.length; i++) { + if(entry.fragments[i] != null) { + count++; + } + } + buf.append("Message ID:").append(entry.msg_id).append("\n\t"); + buf.append("Total Frags:").append(entry.tot_frags).append("\n\t"); + buf.append("Frags Received:").append(count).append("\n\n"); + } + return buf.toString(); + } + } + +} + + Index: 3rdParty_sources/jgroups/org/jgroups/protocols/FlushRsp.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/FlushRsp.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/FlushRsp.java 17 Aug 2012 14:51:05 -0000 1.1 @@ -0,0 +1,26 @@ +// $Id: FlushRsp.java,v 1.1 2012/08/17 14:51:05 marcin Exp $ + +package org.jgroups.protocols; + +import java.util.Vector; + + + + +public class FlushRsp { + public boolean result=true; + public Vector unstable_msgs=new Vector(); + public Vector failed_mbrs=null; // when result is false + + public FlushRsp() {} + + public FlushRsp(boolean result, Vector unstable_msgs, Vector failed_mbrs) { + this.result=result; + this.unstable_msgs=unstable_msgs; + this.failed_mbrs=failed_mbrs; + } + + public String toString() { + return "result=" + result + "\nunstable_msgs=" + unstable_msgs + "\nfailed_mbrs=" + failed_mbrs; + } +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/FragHeader.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/FragHeader.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/FragHeader.java 17 Aug 2012 14:51:06 -0000 1.1 @@ -0,0 +1,62 @@ +package org.jgroups.protocols; + +import org.jgroups.Header; +import org.jgroups.Global; +import org.jgroups.util.Streamable; + +import java.io.*; + +/** + * @author Bela Ban + * @version $Id: FragHeader.java,v 1.1 2012/08/17 14:51:06 marcin Exp $ + */ +public class FragHeader extends Header implements Streamable { + public long id=0; + public int frag_id=0; + public int num_frags=0; + + + public FragHeader() { + } // used for externalization + + public FragHeader(long id, int frag_id, int num_frags) { + this.id=id; + this.frag_id=frag_id; + this.num_frags=num_frags; + } + + public String toString() { + return "[id=" + id + ", frag_id=" + frag_id + ", num_frags=" + num_frags + ']'; + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeLong(id); + out.writeInt(frag_id); + out.writeInt(num_frags); + } + + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + id=in.readLong(); + frag_id=in.readInt(); + num_frags=in.readInt(); + } + + + public void writeTo(DataOutputStream out) throws IOException { + out.writeLong(id); + out.writeInt(frag_id); + out.writeInt(num_frags); + } + + public int size() { + return Global.LONG_SIZE + 2*Global.INT_SIZE; + } + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + id=in.readLong(); + frag_id=in.readInt(); + num_frags=in.readInt(); + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/GMS.java.rmi =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/GMS.java.rmi,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/GMS.java.rmi 17 Aug 2012 14:51:07 -0000 1.1 @@ -0,0 +1,219 @@ + +/* Todo: + + - Discard messages from members not in our current view (?) + +*/ + + +package JavaGroups.JavaStack.Protocols; + +import java.util.*; +import JavaGroups.*; +import JavaGroups.JavaStack.*; + + +public class GMS extends Protocol implements Transportable { + private final int type=Conf.GMS_MSG; + private Properties props=null; + private GmsImpl gms_impl=null; + private MethodInvoker method_invoker=new MethodInvoker(this, false); + private String channel_name=null; + private Queue rsp_queue=new Queue(); + private String gossip_host=null; + private int gossip_port=0; + private GossipClient gossip=null; + private boolean gossiping=false; + + + + public GMS() { + method_invoker.SetMethodLookup(new MethodLookupClos()); + gms_impl=new GmsImpl(this); + method_invoker.AddTargetObject(gms_impl); + } + + public ProtocolStack GetProtocolStack() {return stack;} + public String GetName() {return "GMS";} + public boolean UseGossip() {return gossiping;} + + public Vector FindInitialMembers(String channel_name) { + if(gossip != null) + return gossip.Get(channel_name); + return new Vector(); + } + + + public void Join(Oid new_member) { + gms_impl.Join(new_member); + } + + public void Leave(Oid member) { + gms_impl.Leave(member); + } + + + + /*-------------------------- Interface Transportable -------------------------------*/ + + /** Used e.g. by MethodInvoker to return a response. Tag the message with our type. */ + public void Send(Message msg) throws Exception { + if(msg.GetDest() == null) + msg.SetDest(new Oid(channel_name, type)); + else + ((Oid)msg.GetDest()).SetType(type); + Down(new Event(Event.MSG, msg)); + } + + /** Remove a message from the rsp_queue */ + public Message Receive(long timeout) throws Exception { + try { + return (Message)rsp_queue.Remove(timeout); + } + catch(TimeoutException tex) { + throw tex; + } + catch(QueueClosed closed) { + return null; + } + catch(Exception e) { + System.err.println("GMS.Receive(): " + e); + } + return null; + } + + /*----------------------------------------------------------------------------------*/ + + + + + + + + /*------------------------------- Interface Protocol -------------------------------*/ + /** + * In case of a request, forward the message to the method invoker. In case of a response, + * put it on the response queue, to be retrieved by later Receive() calls. + */ + public void Up(Event evt) { + int msg_type; + Message msg; + Header hdr; + + if(evt.GetType() != Event.MSG) { + PassUp(evt); + return; + } + + msg=(Message)evt.GetArg(); + hdr=msg.RemoveHeader(); + + try { + msg_type=((Oid)msg.GetDest()).GetType(); + if(msg_type == type) { + if(msg.IsResponse()) + rsp_queue.Add(msg); + else + method_invoker.Receive(msg); + return; + } + PassUp(evt); + } + catch(Exception e) { + System.err.println(e); + } + } + + + public void Down(Event evt) { + Message msg; + if(evt.GetType() != Event.MSG) { + HandleDownEvent(evt); + return; + } + msg=(Message)evt.GetArg(); + msg.AddHeader(new Header(gms_impl.GetViewId())); + PassDown(evt); + } + + + + private void HandleDownEvent(Event evt) { + + switch(evt.GetType()) { + + case Event.JOIN: + gms_impl.StartJoin(); + break; + + case Event.LEAVE: + gms_impl.StartLeave((Oid)evt.GetArg()); + PassUp(new Event(Event.LEAVE_OK)); + break; + + default: + PassDown(evt); + } + + } + + + public void StartWork() { + Address local_addr; + + if(stack != null) + channel_name=stack.GetChannelName(); + if(gms_impl == null) { + System.err.println("GMS.StartWork(): gms_impl is null"); + return; + } + if(gossiping) { + if(gossip == null) { + gossip=new GossipClient(stack.GetChannelName(), gossip_host, gossip_port); + local_addr=stack != null ? stack.GetLocalAddress() : null; + if(local_addr != null) + gossip.SetAddress(new Oid(local_addr, stack.GetChannelName())); + else + System.err.println("GMS.StartWork(): starting gossip client, but local " + + "address is null !"); + gossip.Start(); + } + } + } + + public void StopWork() { + if(gossip != null) { + gossip.Stop(); + gossip=null; + } + } + + + /** Setup the Protocol instance acording to the configuration string */ + public void SetProperties(Properties props) { + String str; + this.props=props; + + str=props.getProperty("gossip_host"); + if(str != null) + gossip_host=new String(str); + + str=props.getProperty("gossip_port"); + if(str != null) + gossip_port=new Integer(str).intValue(); + + if(gossip_host != null && gossip_port != 0) + gossiping=true; + + } + + + + public String toString() { + return "Protocol GMS"; + } + + /*----------------------------------------------------------------------------------*/ + + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/HDRS.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/HDRS.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/HDRS.java 17 Aug 2012 14:51:05 -0000 1.1 @@ -0,0 +1,66 @@ +// $Id: HDRS.java,v 1.1 2012/08/17 14:51:05 marcin Exp $ + +package org.jgroups.protocols; + +import org.jgroups.Event; +import org.jgroups.Message; +import org.jgroups.Header; +import org.jgroups.stack.Protocol; + +import java.util.Map; + + +/** + * Example of a protocol layer. Contains no real functionality, can be used as a template. + */ +public class HDRS extends Protocol { + public String getName() {return "HDRS";} + + + private static void printMessage(Message msg, String label) { + StringBuilder sb=new StringBuilder(); + sb.append(label).append(":\n"); + Map hdrs=msg.getHeaders(); + sb.append(print(msg, hdrs)); + System.out.println(sb); + } + + private static String print(Message msg, Map hdrs) { + StringBuilder sb=new StringBuilder(); + int hdrs_size=0; + for(Map.Entry entry: hdrs.entrySet()) { + String name=entry.getKey(); + Header hdr=entry.getValue(); + int size=hdr.size(); + hdrs_size+=size; + sb.append(name).append(": ").append(" ").append(size).append(" bytes\n"); + } + sb.append("headers=").append(hdrs_size).append(", total msg size=").append(msg.size()); + sb.append(", msg payload=").append(msg.getLength()).append("\n"); + return sb.toString(); + } + + + + + public Object up(Event evt) { + if(evt.getType() == Event.MSG) { + Message msg=(Message)evt.getArg(); + printMessage(msg, "up"); + } + return up_prot.up(evt); // Pass up to the layer above us + } + + + + public Object down(Event evt) { + if(evt.getType() == Event.MSG) { + Message msg=(Message)evt.getArg(); + printMessage(msg, "down"); + } + + return down_prot.down(evt); // Pass on to the layer below us + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/HTOTAL.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/HTOTAL.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/HTOTAL.java 17 Aug 2012 14:51:06 -0000 1.1 @@ -0,0 +1,194 @@ +// $Id: HTOTAL.java,v 1.1 2012/08/17 14:51:06 marcin Exp $ + +package org.jgroups.protocols; + +import org.jgroups.*; +import org.jgroups.stack.Protocol; +import org.jgroups.util.Streamable; +import org.jgroups.util.Util; + +import java.io.*; +import java.util.Properties; +import java.util.Vector; + + +/** + * Implementation of UTO-TCP as designed by EPFL. Implements chaining algorithm: each sender sends the message + * to a coordinator who then forwards it to its neighbor on the right, who then forwards it to its neighbor to the right + * etc.

      + * This protocol has not yet been completed and is experimental at best ! + * @author Bela Ban + * @version $Id: HTOTAL.java,v 1.1 2012/08/17 14:51:06 marcin Exp $ + */ +public class HTOTAL extends Protocol { + Address coord=null; + Address neighbor=null; // to whom do we forward the message (member to the right, or null if we're at the tail) + Address local_addr=null; + Vector mbrs=new Vector(); + boolean is_coord=false; + private boolean use_multipoint_forwarding=false; + + + + + public HTOTAL() { + } + + public final String getName() { + return "HTOTAL"; + } + + public boolean setProperties(Properties props) { + String str; + + super.setProperties(props); + str=props.getProperty("use_multipoint_forwarding"); + if(str != null) { + use_multipoint_forwarding=Boolean.valueOf(str).booleanValue(); + props.remove("use_multipoint_forwarding"); + } + + if(!props.isEmpty()) { + log.error("TCP.setProperties(): the following properties are not recognized: " + props); + return false; + } + return true; + } + + public Object down(Event evt) { + switch(evt.getType()) { + case Event.VIEW_CHANGE: + determineCoordinatorAndNextMember((View)evt.getArg()); + break; + case Event.MSG: + Message msg=(Message)evt.getArg(); + Address dest=msg.getDest(); + if(dest == null || dest.isMulticastAddress()) { // only process multipoint messages + if(coord == null) + log.error("coordinator is null, cannot send message to coordinator"); + else { + msg.setSrc(local_addr); + forwardTo(coord, msg); + } + return null; // handled here, don't pass down by default + } + break; + } + return down_prot.down(evt); + } + + public Object up(Event evt) { + switch(evt.getType()) { + case Event.SET_LOCAL_ADDRESS: + local_addr=(Address)evt.getArg(); + break; + case Event.VIEW_CHANGE: + determineCoordinatorAndNextMember((View)evt.getArg()); + break; + case Event.MSG: + Message msg=(Message)evt.getArg(); + HTotalHeader hdr=(HTotalHeader)msg.getHeader(getName()); + + if(hdr == null) + break; // probably a unicast message, just pass it up + + Message copy=msg.copy(false); // do not copy the buffer + if(use_multipoint_forwarding) { + copy.setDest(null); + down_prot.down(new Event(Event.MSG, copy)); + } + else { + if(neighbor != null) { + forwardTo(neighbor, copy); + } + } + + msg.setDest(hdr.dest); // set destination to be the original destination + msg.setSrc(hdr.src); // set sender to be the original sender (important for retransmission requests) + + return up_prot.up(evt); // <-- we modify msg directly inside evt + } + return up_prot.up(evt); + } + + private void forwardTo(Address destination, Message msg) { + HTotalHeader hdr=(HTotalHeader)msg.getHeader(getName()); + + if(hdr == null) { + hdr=new HTotalHeader(msg.getDest(), msg.getSrc()); + msg.putHeader(getName(), hdr); + } + msg.setDest(destination); + if(log.isTraceEnabled()) + log.trace("forwarding message to " + destination + ", hdr=" + hdr); + down_prot.down(new Event(Event.MSG, msg)); + } + + + private void determineCoordinatorAndNextMember(View v) { + Object tmp; + Address retval=null; + + mbrs.clear(); + mbrs.addAll(v.getMembers()); + + coord=(Address)(mbrs != null && !mbrs.isEmpty()? mbrs.firstElement() : null); + is_coord=coord != null && local_addr != null && coord.equals(local_addr); + + if(mbrs == null || mbrs.size() < 2 || local_addr == null) + neighbor=null; + else { + for(int i=0; i < mbrs.size(); i++) { + tmp=mbrs.elementAt(i); + if(local_addr.equals(tmp)) { + if(i + 1 >= mbrs.size()) + retval=null; // we don't wrap, last member is null + else + retval=(Address)mbrs.elementAt(i + 1); + break; + } + } + } + neighbor=retval; + if(log.isTraceEnabled()) + log.trace("coord=" + coord + ", neighbor=" + neighbor); + } + + + public static class HTotalHeader extends Header implements Streamable { + Address dest, src; + + public HTotalHeader() { + } + + public HTotalHeader(Address dest, Address src) { + this.dest=dest; + this.src=src; + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeObject(dest); + out.writeObject(src); + } + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + dest=(Address)in.readObject(); + src=(Address)in.readObject(); + } + + public void writeTo(DataOutputStream out) throws IOException { + Util.writeAddress(dest, out); + Util.writeAddress(src, out); + } + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + dest=Util.readAddress(in); + src=Util.readAddress(in); + } + + public String toString() { + return "dest=" + dest + ", src=" + src; + } + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/JMS.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/JMS.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/JMS.java 17 Aug 2012 14:51:07 -0000 1.1 @@ -0,0 +1,691 @@ +// $Id: JMS.java,v 1.1 2012/08/17 14:51:07 marcin Exp $ + +package org.jgroups.protocols; + +import org.jgroups.Address; +import org.jgroups.Event; +import org.jgroups.Message; +import org.jgroups.View; +import org.jgroups.stack.Protocol; +import org.jgroups.util.Util; + +import javax.naming.Context; +import javax.naming.InitialContext; +import java.io.*; +import java.util.Hashtable; +import java.util.Properties; +import java.util.Vector; + +/** + * Implementation of the transport protocol using the Java Message Service (JMS). + * This implementation depends on the JMS server that will distribute messages + * published to the specific topic to all topic subscribers. + *

      + * Protocol parameters are: + *

        + *
      • topicName - (required), full JNDI name of the topic to be + * used for message publishing; + * + *
      • cf - (optional), full JNDI name of the topic connection + * factory that will create topic connection, default value is + * "ConnectionFactory"; + * + *
      • jndiCtx - (optional), value of the + * javax.naming.Context.INITIAL_CONTEXT_FACTORY property; you can + * specify it as the JVM system property + * -Djava.naming.factory.initial=factory.class.Name; + * + *
      • providerURL - (optional), value of the + * javax.naming.Context.PROVIDER_URL property; you can specify it + * as the JVM system property -Djava.naming.provider.url=some_url + * + *
      • ttl - (required), time to live in milliseconds. Default + * value is 0, that means that messages will never expire and will be + * accumulated by a JMS server. + * + *
      + * + *

      + * Note, when you are using the JMS protocol, try to avoid using protocols + * that open server socket connections, like FD_SOCK. I belive that FD is more + * appropriate failure detector for JMS case. + * + * @author Roman Rokytskyy (rrokytskyy@acm.org) + */ +public class JMS extends Protocol implements javax.jms.MessageListener { + + public static final + String DEFAULT_CONNECTION_FACTORY = "ConnectionFactory"; + + public static final + String INIT_CONNECTION_FACTORY = "cf"; + + public static final + String INIT_TOPIC_NAME = "topicName"; + + public static final + String INIT_JNDI_CONTEXT = "jndiCtx"; + + public static final + String INIT_PROVIDER_URL = "providerURL"; + + public static final + String TIME_TO_LIVE = "ttl"; + + public static final + String GROUP_NAME_PROPERTY = "jgroups_group_name"; + + public static final + String SRC_PROPERTY = "src"; + + public static final + String DEST_PROPERTY = "dest"; + + private final Vector members = new Vector(); + + private javax.jms.TopicConnectionFactory connectionFactory; + private javax.jms.Topic topic; + + private javax.jms.TopicConnection connection; + + private javax.jms.TopicSession session; + private javax.jms.TopicPublisher publisher; + private javax.jms.TopicSubscriber subscriber; + + private String cfName; + private String topicName; + private String initCtxFactory; + private String providerUrl; + private long timeToLive; + + private Context ctx; + + private String group_addr; + private Address local_addr; + private Address mcast_addr; + + private final ByteArrayOutputStream out_stream = new ByteArrayOutputStream(65535); + + private static final java.util.Random RND = new java.util.Random(); + + /** + * Empty constructor. + */ + public JMS() { + } + + /** + * Get the name of the protocol. + * + * @return always returns the "JMS" string. + */ + public String getName() { + return "JMS"; + } + + /** + * Get the string representation of the protocol. + * + * @return string representation of the protocol (not very useful though). + */ + public String toString() { + return "Protocol JMS(local address: " + local_addr + ')'; + } + + /** + * Set protocol properties. Properties are: + *

        + *
      • topicName - (required), full JNDI name of the topic to be + * used for message publishing; + * + *
      • cf - (optional), full JNDI name of the topic connection + * factory that will create topic connection, default value is + * "ConnectionFactory"; + * + *
      • jndiCtx - (optional), value of the + * javax.naming.Context.INITIAL_CONTEXT_FACTORY property; you can + * specify it as the JVM system property + * -Djava.naming.factory.initial=factory.class.Name; + * + *
      • providerURL - (optional), value of the + * javax.naming.Context.PROVIDER_URL property; you can specify it + * as the JVM system property -Djava.naming.provider.url=some_url + *
      + * + */ + public boolean setProperties(Properties props) { + super.setProperties(props); + cfName = props.getProperty(INIT_CONNECTION_FACTORY, + DEFAULT_CONNECTION_FACTORY); + + props.remove(INIT_CONNECTION_FACTORY); + + topicName = props.getProperty(INIT_TOPIC_NAME); + + if (topicName == null) + throw new IllegalArgumentException( + "JMS topic has not been specified."); + + props.remove(INIT_TOPIC_NAME); + + initCtxFactory = props.getProperty(INIT_JNDI_CONTEXT); + props.remove(INIT_JNDI_CONTEXT); + + providerUrl = props.getProperty(INIT_PROVIDER_URL); + props.remove(INIT_PROVIDER_URL); + + String ttl = props.getProperty(TIME_TO_LIVE); + + if (ttl == null) { + if(log.isErrorEnabled()) log.error("ttl property not found."); + return false; + } + + props.remove(TIME_TO_LIVE); + + // try to parse ttl property + try { + + timeToLive = Long.parseLong(ttl); + + } catch(NumberFormatException nfex) { + if(log.isErrorEnabled()) log.error("ttl property does not contain numeric value."); + + return false; + } + + return props.isEmpty(); + } + + + + + /** + * Implementation of the javax.jms.MessageListener interface. + * This method receives the JMS message, checks the destination group name. + * If the group name is the same as the group name of this channel, it + * checks the destination address. If destination address is either + * multicast or is the same as local address then message is unwrapped and + * passed up the protocol stack. Otherwise it is ignored. + * + * @param jmsMessage instance of javax.jms.Message. + */ + public void onMessage(javax.jms.Message jmsMessage) { + try { + String groupName = jmsMessage.getStringProperty(GROUP_NAME_PROPERTY); + + // there might be other messages in this topic + if (groupName == null) + return; + + + if(log.isDebugEnabled()) log.debug("Got message for group [" + + groupName + ']' + ", my group is [" + group_addr + ']'); + + // not our message, ignore it + if (!group_addr.equals(groupName)) + return; + + JMSAddress src = + jmsMessage.getStringProperty(SRC_PROPERTY) != null ? + new JMSAddress(jmsMessage.getStringProperty(SRC_PROPERTY)) : + null; + + JMSAddress dest = + jmsMessage.getStringProperty(DEST_PROPERTY) != null ? + new JMSAddress(jmsMessage.getStringProperty(DEST_PROPERTY)) : + null; + + // if message is unicast message and I'm not the destination - ignore + if (src != null && dest != null && + !dest.equals(local_addr) && !dest.isMulticastAddress()) + return; + + + if (jmsMessage instanceof javax.jms.ObjectMessage) { + byte[] buf = (byte[])((javax.jms.ObjectMessage)jmsMessage).getObject(); + + ByteArrayInputStream inp_stream=new ByteArrayInputStream(buf); + DataInputStream inp=new DataInputStream(inp_stream); + + Message msg=new Message(); + msg.readFrom(inp); + + Event evt=new Event(Event.MSG, msg); + + // +++ remove + if(log.isDebugEnabled()) log.debug("Message is " + msg + + ", headers are " + msg.printHeaders()); + + up_prot.up(evt); + } + } catch(javax.jms.JMSException ex) { + ex.printStackTrace(); + if(log.isErrorEnabled()) log.error("JMSException : " + ex.toString()); + } catch(IOException ioex) { + ioex.printStackTrace(); + if(log.isErrorEnabled()) log.error("IOException : " + ioex.toString()); + } + catch(InstantiationException e) { + e.printStackTrace(); + } + catch(IllegalAccessException e) { + e.printStackTrace(); + } + } + + /** + * Handle down event, if it is not a Event.MSG type. + * + * @param evt event to handle. + */ + protected Object handleDownEvent(Event evt) { + switch(evt.getType()) { + + // we do not need this at present time, maybe in the future + case Event.TMP_VIEW: + case Event.VIEW_CHANGE: + synchronized(members) { + members.removeAllElements(); + Vector tmpvec=((View)evt.getArg()).getMembers(); + for(int i=0; i < tmpvec.size(); i++) + members.addElement(tmpvec.elementAt(i)); + } + break; + + case Event.CONNECT: + group_addr=(String)evt.getArg(); + break; + } + return null; + } + + /** + * Called by the protocol above this. We check the event type, and if it is + * message, we publish it in the topic, otherwise we let the + * {@link #handleDownEvent(Event)} take care of it. + * + * @param evt event to process. + */ + public Object down(Event evt) { + + if(log.isTraceEnabled()) + log.trace("event is " + evt + ", group_addr=" + group_addr + ", hdrs are " + Util.printEvent(evt)); + + // handle all non-message events + if(evt.getType() != Event.MSG) { + return handleDownEvent(evt); + } + + // extract message + Message msg=(Message)evt.getArg(); + + // publish the message to the topic + sendMessage(msg); + return null; + } + + + + /** + * Publish message in the JMS topic. We set the message source and + * destination addresses if they were null. + * + * @param msg message to publish. + */ + protected void sendMessage(Message msg) { + try { + if (msg.getSrc() == null) + msg.setSrc(local_addr); + + if (msg.getDest() == null) + msg.setDest(mcast_addr); + + + if(log.isInfoEnabled()) log.info("msg is " + msg); + + // convert the message into byte array. + out_stream.reset(); + + DataOutputStream out=new DataOutputStream(out_stream); + msg.writeTo(out); + out.flush(); + + byte[] buf = out_stream.toByteArray(); + + javax.jms.ObjectMessage jmsMessage = session.createObjectMessage(); + + // set the payload + jmsMessage.setObject(buf); + + // set the group name + jmsMessage.setStringProperty(GROUP_NAME_PROPERTY, group_addr); + + // if the source is JMSAddress, copy it to the header + if (msg.getSrc() instanceof JMSAddress) + jmsMessage.setStringProperty( + SRC_PROPERTY, msg.getSrc().toString()); + + // if the destination is JMSAddress, copy it to the header + if (msg.getDest() instanceof JMSAddress) + jmsMessage.setStringProperty( + DEST_PROPERTY, msg.getDest().toString()); + + // publish message + publisher.publish(jmsMessage); + + } catch(javax.jms.JMSException ex) { + if(log.isErrorEnabled()) log.error("JMSException : " + ex.toString()); + } catch(IOException ioex) { + if(log.isErrorEnabled()) log.error("IOException : " + ioex.toString()); + } + } + + /** + * Start the JMS protocol. This method instantiates the JNDI initial context + * and looks up the topic connection factory and topic itself. If this step + * is successful, it creates a connection to JMS server, opens a session + * and obtains publisher and subscriber instances. + * + * @throws javax.jms.JMSException if something goes wrong with JMS. + * @throws javax.naming.NamingException if something goes wrong with JNDI. + * @throws IllegalArgumentException if the connection factory or topic + * cannot be found under specified names. + */ + public void start() throws Exception + { + if (initCtxFactory != null && providerUrl != null) { + Hashtable env = new Hashtable(); + env.put(Context.INITIAL_CONTEXT_FACTORY, initCtxFactory); + env.put(Context.PROVIDER_URL, providerUrl); + + ctx = new InitialContext(env); + } else + ctx = new InitialContext(); + + connectionFactory = (javax.jms.TopicConnectionFactory)ctx.lookup(cfName); + + if (connectionFactory == null) + throw new IllegalArgumentException( + "Topic connection factory cannot be found in JNDI."); + + topic = (javax.jms.Topic)ctx.lookup(topicName); + + if (topic == null) + throw new IllegalArgumentException("Topic cannot be found in JNDI."); + + connection = connectionFactory.createTopicConnection(); + + boolean addressAssigned = false; + + // check if JMS connection contains client ID, + // if not, try to assign randomly generated one + /*while(!addressAssigned) { + if (connection.getClientID() != null) + addressAssigned = true; + else + try { + connection.setClientID(generateLocalAddress()); + addressAssigned = true; + } catch(javax.jms.InvalidClientIDException ex) { + // duplicate... ok, let's try again + } + }*/ + + + // Patch below submitted by Greg Woolsey + // Check if JMS connection contains client ID, if not, try to assign randomly generated one + // setClientID() must be the first method called on a new connection, per the JMS spec. + // If the client ID is already set, this will throw IllegalStateException and keep the original value. + while(!addressAssigned) { + try { + connection.setClientID(generateLocalAddress()); + addressAssigned = true; + } catch (javax.jms.IllegalStateException e) { + // expected if connection already has a client ID. + addressAssigned = true; + } catch(javax.jms.InvalidClientIDException ex) { + // duplicate... OK, let's try again + } + } + + local_addr = new JMSAddress(connection.getClientID(), false); + mcast_addr = new JMSAddress(topicName, true); + + session = connection.createTopicSession(false, + javax.jms.Session.AUTO_ACKNOWLEDGE); + + publisher = session.createPublisher(topic); + publisher.setTimeToLive(timeToLive); + + subscriber = session.createSubscriber(topic); + subscriber.setMessageListener(this); + + connection.start(); + + up_prot.up(new Event(Event.SET_LOCAL_ADDRESS, local_addr)); + } + + /** + * Stops the work of the JMS protocol. This method closes JMS session and + * connection and deregisters itself from the message notification. + */ + public void stop() { + + if(log.isInfoEnabled()) log.info("finishing JMS transport layer."); + + try { + connection.stop(); + subscriber.setMessageListener(null); + session.close(); + connection.close(); + } + catch(Throwable ex) { + if(log.isErrorEnabled()) log.error("exception is " + ex); + } + } + + /** + * Generate random local address. This method takes host name and appends + * it with randomly generated integer. + * + * @return randomly generated local address. + */ + protected String generateLocalAddress() throws java.net.UnknownHostException { + String hostName = java.net.InetAddress.getLocalHost().getHostName(); + + int rndPort = RND.nextInt(65535); + + return hostName + ':' + rndPort; + } + + /** + * Simple {@link Address} representing the JMS node ID or JMS topic group. + */ + public static class JMSAddress implements Address { + + private static final long serialVersionUID = -2311584492745452246L; + + private String address; + private boolean isMCast; + + + /** + * Empty constructor to allow externalization work. + */ + public JMSAddress() { + } + + + /** + * Create instance of this class for given address string. + * + * Current implementation uses a hash mark '#' to determine + * if the address is a unicast or multicast. Therefore, this character is + * considered as reserved and is not allowed in the address + * parameter passed to this constructor. + * + * @param address string representing the address of the node connected + * to the JMS topic, usually, a value of + * connection.getClientID(), where the connection is + * instance of javax.jms.TopicConnection. + * + * @param isMCast true if the address is multicast address, + * otherwise - false. + */ + JMSAddress(String address, boolean isMCast) { + this.address = address; + this.isMCast = isMCast; + } + + + + /** + * Reconstruct the address from the string representation. If the + * str starts with '#', address is considered + * as unicast, and node address is the substring after '#'. + * Otherwise, address is multicast and address is str + * itself. + * + * @param str string used to reconstruct the instance. + */ + JMSAddress(String str) { + if (str.startsWith("#")) { + address = str.substring(1); + isMCast = false; + } else { + address = str; + isMCast = true; + } + } + + /** + * Get the node address. + * + * @return node address in the form passed to the constructor + * {@link #JMS.JMSAddress(String, boolean)}. + */ + public String getAddress() { return address; } + + /** + * Set the node address. + * + * @param address new node address. + */ + public void setAddress(String address) { this.address = address; } + + /** + * Is the address a multicast address? + * + * @return true if the address is multicast address. + */ + public boolean isMulticastAddress() { + return isMCast; + } + + public int size() { + return 22; + } + + /** + * Clone the object. + */ + protected Object clone() throws CloneNotSupportedException { + return new JMSAddress(address, isMCast); + } + + /** + * Compare this object to o. It is possible to compare only + * addresses of the same class. Also they both should be either + * multicast or unicast addresses. + * + * @return value compliant with the {@link Comparable#compareTo(Object)} + * specififaction. + */ + public int compareTo(Object o) throws ClassCastException { + if (!(o instanceof JMSAddress)) + throw new ClassCastException("Cannot compare different classes."); + + JMSAddress that = (JMSAddress)o; + + if (that.isMCast != this.isMCast) + throw new ClassCastException( + "Addresses are different: one is multicast, and one is not"); + + return this.address.compareTo(that.address); + } + + /** + * Test is this object is equal to obj. + * + * @return true iff the obj is + * JMSAddress, node addresses are equal and they both are + * either multicast or unicast addresses. + */ + public boolean equals(Object obj) { + if (obj == this) return true; + + if (!(obj instanceof JMSAddress)) + return false; + + JMSAddress that = (JMSAddress)obj; + + if (this.isMCast) + return this.isMCast == that.isMCast; + else + return !(this.address == null || that.address == null) && this.address.equals(that.address) && + this.isMCast == that.isMCast; + } + + /** + * Get the hash code of this address. + * + * @return hash code of this object. + */ + public int hashCode() { + return toString().hashCode(); + } + + /** + * Read object from external input. + */ + public void readExternal(ObjectInput in) + throws IOException, ClassNotFoundException + { + address = (String)in.readObject(); + isMCast = in.readBoolean(); + } + + /** + * Get the string representation of the address. The following property + * holds: a2.equals(a1) is always true, where + * a2 is + * JMSAddress a2 = new JMSAddress(a1.toString()); + * + * @return string representation of the address. + */ + public String toString() { + return !isMCast ? '#' + address : address; + } + + /** + * Write the object to external output. + */ + public void writeExternal(ObjectOutput out) throws IOException { + out.writeObject(address); + out.writeBoolean(isMCast); + } + + + public void writeTo(DataOutputStream outstream) throws IOException { + outstream.writeUTF(address); + outstream.writeBoolean(isMCast); + } + + public void readFrom(DataInputStream instream) throws IOException, IllegalAccessException, InstantiationException { + address=instream.readUTF(); + isMCast=instream.readBoolean(); + } + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/LOOPBACK.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/LOOPBACK.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/LOOPBACK.java 17 Aug 2012 14:51:06 -0000 1.1 @@ -0,0 +1,116 @@ +// $Id: LOOPBACK.java,v 1.1 2012/08/17 14:51:06 marcin Exp $ + +package org.jgroups.protocols; + + +import org.jgroups.Address; +import org.jgroups.Event; +import org.jgroups.Message; +import org.jgroups.stack.IpAddress; +import org.jgroups.util.Util; + + +/** + Makes copies of outgoing messages, swaps sender and receiver and sends the message back up the stack. + */ +public class LOOPBACK extends TP { + private String group_addr=null; + + public LOOPBACK() { + } + + + public String toString() { + return "LOOPBACK(local address: " + local_addr + ')'; + } + + public void sendToAllMembers(byte[] data, int offset, int length) throws Exception { + } + + public void sendToSingleMember(Address dest, byte[] data, int offset, int length) throws Exception { + } + + public String getInfo() { + return null; + } + + public void postUnmarshalling(Message msg, Address dest, Address src, boolean multicast) { + } + + public void postUnmarshallingList(Message msg, Address dest, boolean multicast) { + } + + /*------------------------------ Protocol interface ------------------------------ */ + + public String getName() { + return "LOOPBACK"; + } + + + + public void init() throws Exception { + super.init(); +// local_addr=new IpAddress("localhost", 10000) { // fake address +// public String toString() { +// return ""; +// } +// }; + + //local_addr=new org.jgroups.stack.IpAddress("localhost", 10000); // fake address + local_addr = new IpAddress(12345); + } + + public void destroy() { + System.out.println("destroy();"); + try { + timer.stop(); + } + catch(InterruptedException e) { + e.printStackTrace(); + } + } + + public void start() throws Exception { + up_prot.up(new Event(Event.SET_LOCAL_ADDRESS, local_addr)); + } + + + /** + * Caller by the layer above this layer. Usually we just put this Message + * into the send queue and let one or more worker threads handle it. A worker thread + * then removes the Message from the send queue, performs a conversion and adds the + * modified Message to the send queue of the layer below it, by calling Down). + */ + public Object down(Event evt) { + if(log.isTraceEnabled()) + log.trace("event is " + evt + ", group_addr=" + group_addr + + ", time is " + System.currentTimeMillis() + ", hdrs: " + Util.printEvent(evt)); + + switch(evt.getType()) { + + case Event.MSG: + Message msg=(Message)evt.getArg(); + Message rsp=msg.copy(); + if(rsp.getSrc() == null) + rsp.setSrc(local_addr); + + //dest_addr=msg.getDest(); + //rsp.setDest(local_addr); + //rsp.setSrc(dest_addr != null ? dest_addr : local_addr); + up(new Event(Event.MSG, rsp)); + break; + + case Event.CONNECT: + case Event.CONNECT_WITH_STATE_TRANSFER: + group_addr=(String)evt.getArg(); + break; + } + return null; + } + + + + /*--------------------------- End of Protocol interface -------------------------- */ + + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/MERGE2.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/MERGE2.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/MERGE2.java 17 Aug 2012 14:51:05 -0000 1.1 @@ -0,0 +1,270 @@ +// $Id: MERGE2.java,v 1.1 2012/08/17 14:51:05 marcin Exp $ + +package org.jgroups.protocols; + + +import org.jgroups.Address; +import org.jgroups.Event; +import org.jgroups.View; +import org.jgroups.annotations.GuardedBy; +import org.jgroups.stack.Protocol; +import org.jgroups.util.TimeScheduler; +import org.jgroups.util.Util; + +import java.util.List; +import java.util.Properties; +import java.util.Vector; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + + +/** + * Protocol to discover subgroups; e.g., existing due to a network partition (that healed). Example: group + * {p,q,r,s,t,u,v,w} is split into 3 subgroups {p,q}, {r,s,t,u} and {v,w}. This protocol will eventually send + * a MERGE event with the coordinators of each subgroup up the stack: {p,r,v}. Note that - depending on the time + * of subgroup discovery - there could also be 2 MERGE events, which first join 2 of the subgroups, and then the + * resulting group to the last subgroup. The real work of merging the subgroups into one larger group is done + * somewhere above this protocol (typically in the GMS protocol).

      + * This protocol works as follows: + *

        + *
      • If coordinator: periodically retrieve the initial membership (using the FIND_INITIAL_MBRS event provided e.g. + * by PING or TCPPING protocols. This list contains {coord,addr} pairs. + *
      • If there is more than 1 coordinator: + *
          + *
        1. Get all coordinators + *
        2. Create a MERGE event with the list of coordinators as argument + *
        3. Send the event up the stack + *
        + *
      + * + *

      + * + * Requires: FIND_INITIAL_MBRS event from below
      + * Provides: sends MERGE event with list of coordinators up the stack
      + * @author Bela Ban, Oct 16 2001 + */ +public class MERGE2 extends Protocol { + private Address local_addr=null; + private final FindSubgroupsTask task=new FindSubgroupsTask(); // task periodically executing as long as we are coordinator + private long min_interval=5000; // minimum time between executions of the FindSubgroups task + private long max_interval=20000; // maximum time between executions of the FindSubgroups task + private volatile boolean is_coord=false; + private volatile boolean use_separate_thread=false; // Use a new thread to send the MERGE event up the stack + private TimeScheduler timer; + + public void init() throws Exception { + timer=getTransport().getTimer(); + if(timer == null) + throw new Exception("timer cannot be retrieved from protocol stack"); + } + + + public String getName() { + return "MERGE2"; + } + + public long getMinInterval() { + return min_interval; + } + + public void setMinInterval(long i) { + min_interval=i; + } + + public long getMaxInterval() { + return max_interval; + } + + public void setMaxInterval(long l) { + max_interval=l; + } + + + public boolean setProperties(Properties props) { + String str; + + super.setProperties(props); + str=props.getProperty("min_interval"); + if(str != null) { + min_interval=Long.parseLong(str); + props.remove("min_interval"); + } + + str=props.getProperty("max_interval"); + if(str != null) { + max_interval=Long.parseLong(str); + props.remove("max_interval"); + } + + if(min_interval <= 0 || max_interval <= 0) { + if(log.isErrorEnabled()) log.error("min_interval and max_interval have to be > 0"); + return false; + } + if(max_interval <= min_interval) { + if(log.isErrorEnabled()) log.error("max_interval has to be greater than min_interval"); + return false; + } + + str=props.getProperty("use_separate_thread"); + if(str != null) { + use_separate_thread=Boolean.valueOf(str).booleanValue(); + props.remove("use_separate_thread"); + } + + if(!props.isEmpty()) { + log.error("the following properties are not recognized: " + props); + return false; + } + return true; + } + + + public Vector requiredDownServices() { + Vector retval=new Vector(1); + retval.addElement(new Integer(Event.FIND_INITIAL_MBRS)); + return retval; + } + + + public void stop() { + is_coord=false; + task.stop(); + } + + + + public Object up(Event evt) { + switch(evt.getType()) { + + case Event.SET_LOCAL_ADDRESS: + local_addr=(Address)evt.getArg(); + return up_prot.up(evt); + + default: + return up_prot.up(evt); // Pass up to the layer above us + } + } + + + public Object down(Event evt) { + switch(evt.getType()) { + + case Event.VIEW_CHANGE: + Object ret=down_prot.down(evt); + Vector

      mbrs=((View)evt.getArg()).getMembers(); + if(mbrs == null || mbrs.isEmpty() || local_addr == null) { + task.stop(); + return ret; + } + Address coord=mbrs.elementAt(0); + if(coord.equals(local_addr)) { + is_coord=true; + task.start(); // start task if we became coordinator (doesn't start if already running) + } + else { + // if we were coordinator, but are no longer, stop task. this happens e.g. when we merge and someone + // else becomes the new coordinator of the merged group + if(is_coord) { + is_coord=false; + } + task.stop(); + } + return ret; + + default: + return down_prot.down(evt); // Pass on to the layer below us + } + } + + + /** + * Task periodically executing (if role is coordinator). Gets the initial membership and determines + * whether there are subgroups (multiple coordinators for the same group). If yes, it sends a MERGE event + * with the list of the coordinators up the stack + */ + private class FindSubgroupsTask { + @GuardedBy("this") + private Future future; + + public synchronized void start() { + if(future == null || future.isDone()) { + future=timer.scheduleWithFixedDelay(new Runnable() { + public void run() { + findAndNotify(); + } + }, 0, computeInterval(), TimeUnit.MILLISECONDS); + } + } + + public synchronized void stop() { + if(future != null) { + future.cancel(true); + future=null; + } + } + + public void findAndNotify() { + List initial_mbrs=findInitialMembers(); + + Vector
      coords=detectMultipleCoordinators(initial_mbrs); + if(coords.size() > 1) { + if(log.isDebugEnabled()) + log.debug(local_addr + " found multiple coordinators: " + coords + "; sending up MERGE event"); + final Event evt=new Event(Event.MERGE, coords); + if(use_separate_thread) { + Thread merge_notifier=new Thread() { + public void run() { + up_prot.up(evt); + } + }; + merge_notifier.setDaemon(true); + merge_notifier.setName("merge notifier thread"); + merge_notifier.start(); + } + else { + up_prot.up(evt); + } + } + if(log.isTraceEnabled()) + log.trace("MERGE2.FindSubgroups thread terminated (local_addr=" + local_addr + ")"); + } + + /** + * Returns a random value within [min_interval - max_interval] + */ + long computeInterval() { + return min_interval + Util.random(max_interval - min_interval); + } + + /** + * Returns a list of PingRsp pairs. + */ + List findInitialMembers() { + PingRsp tmp=new PingRsp(local_addr, local_addr, true); + List retval=(List)down_prot.down(new Event(Event.FIND_INITIAL_MBRS)); + if(retval != null && is_coord && local_addr != null && !retval.contains(tmp)) + retval.add(tmp); + return retval; + } + + /** + * Finds out if there is more than 1 coordinator in the initial_mbrs vector (contains PingRsp elements). + * @param initial_mbrs A list of PingRsp pairs + * @return Vector A list of the coordinators (Addresses) found. Will contain just 1 element for a correct + * membership, and more than 1 for multiple coordinators + */ + Vector
      detectMultipleCoordinators(List initial_mbrs) { + Vector
      ret=new Vector
      (11); + if(initial_mbrs != null) { + for(PingRsp response:initial_mbrs) { + if(response.isServer()) { + Address coord=response.getCoordAddress(); + if(!ret.contains(coord)) + ret.add(coord); + } + } + } + return ret; + } + } +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/MERGE3.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/MERGE3.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/MERGE3.java 17 Aug 2012 14:51:08 -0000 1.1 @@ -0,0 +1,264 @@ +// $Id: MERGE3.java,v 1.1 2012/08/17 14:51:08 marcin Exp $ + +package org.jgroups.protocols; + + +import org.jgroups.*; +import org.jgroups.stack.Protocol; +import org.jgroups.util.TimeScheduler; +import org.jgroups.util.Util; + +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.util.*; +import java.util.concurrent.Future; + + +/** + * Protocol to discover subgroups; e.g., existing due to a network partition (that healed). Example: group + * {p,q,r,s,t,u,v,w} is split into 3 subgroups {p,q}, {r,s,t,u} and {v,w}. This protocol will eventually send + * a MERGE event with the coordinators of each subgroup up the stack: {p,r,v}. Note that - depending on the time + * of subgroup discovery - there could also be 2 MERGE events, which first join 2 of the subgroups, and then the + * resulting group to the last subgroup. The real work of merging the subgroups into one larger group is done + * somewhere above this protocol (typically in the GMS protocol).

      + * This protocol works as follows: + *

        + *
      • If coordinator: periodically broadcast a "I'm the coordinator" message. If a coordinator receives such + * a message, it immediately initiates a merge by sending up a MERGE event + *

        + * + * Provides: sends MERGE event with list of coordinators up the stack
        + * @author Bela Ban, Oct 16 2001 + */ +public class MERGE3 extends Protocol { + Address local_addr=null; + long min_interval=5000; // minimum time between executions of the FindSubgroups task + long max_interval=20000; // maximum time between executions of the FindSubgroups task + boolean is_coord=false; + final Vector mbrs=new Vector(); + TimeScheduler timer=null; + Future announcer_task_future=null; + CoordinatorAnnouncer announcer_task=null; + final Set announcements=Collections.synchronizedSet(new HashSet()); + + /** Use a new thread to send the MERGE event up the stack */ + boolean use_separate_thread=false; + + + + + public String getName() { + return "MERGE3"; + } + + + public boolean setProperties(Properties props) { + String str; + + super.setProperties(props); + str=props.getProperty("min_interval"); + if(str != null) { + min_interval=Long.parseLong(str); + props.remove("min_interval"); + } + + str=props.getProperty("max_interval"); + if(str != null) { + max_interval=Long.parseLong(str); + props.remove("max_interval"); + } + + if(min_interval <= 0 || max_interval <= 0) { + if(log.isErrorEnabled()) log.error("min_interval and max_interval have to be > 0"); + return false; + } + if(max_interval <= min_interval) { + if(log.isErrorEnabled()) log.error("max_interval has to be greater than min_interval"); + return false; + } + + str=props.getProperty("use_separate_thread"); + if(str != null) { + use_separate_thread=Boolean.valueOf(str).booleanValue(); + props.remove("use_separate_thread"); + } + + if(!props.isEmpty()) { + log.error("MERGE2.setProperties(): the following properties are not recognized: " + props); + return false; + } + return true; + } + + public void init() throws Exception { + timer=getTransport().getTimer(); + } + + + + public Object up(Event evt) { + switch(evt.getType()) { + + case Event.MSG: + Message msg=(Message)evt.getArg(); + CoordAnnouncement hdr=(CoordAnnouncement)msg.getHeader(getName()); + if(hdr != null) { + if(hdr.coord_addr != null && is_coord) { + boolean contains; + contains=announcements.contains(hdr.coord_addr); + announcements.add(hdr.coord_addr); + if(log.isDebugEnabled()) { + if(contains) + log.debug("discarded duplicate announcement: " + hdr.coord_addr + + ", announcements=" + announcements); + else + log.debug("received announcement: " + hdr.coord_addr + ", announcements=" + announcements); + } + + if(announcements.size() > 1 && is_coord) { + processAnnouncements(); + } + } + return null; + } + else + return up_prot.up(evt); + + case Event.SET_LOCAL_ADDRESS: + local_addr=(Address)evt.getArg(); + break; + } + + return up_prot.up(evt); + } + + + public Object down(Event evt) { + Vector tmp; + Address coord; + + switch(evt.getType()) { + + case Event.VIEW_CHANGE: + down_prot.down(evt); + tmp=((View)evt.getArg()).getMembers(); + mbrs.clear(); + mbrs.addAll(tmp); + coord=(Address)mbrs.elementAt(0); + if(coord.equals(local_addr)) { + if(is_coord == false) { + is_coord=true; + startCoordAnnouncerTask(); + } + } + else { + if(is_coord == true) { + is_coord=false; + stopCoordAnnouncerTask(); + } + } + break; + } + return down_prot.down(evt); + } + + + void startCoordAnnouncerTask() { + if(announcer_task_future == null || announcer_task_future.isDone()) { + announcements.add(local_addr); + announcer_task=new CoordinatorAnnouncer(); + announcer_task_future=timer.scheduleWithDynamicInterval(announcer_task); + if(log.isDebugEnabled()) + log.debug("coordinator announcement task started, announcements=" + announcements); + } + } + + void stopCoordAnnouncerTask() { + if(announcer_task_future != null) { + announcer_task_future.cancel(false); + announcer_task_future=null; + } + announcer_task=null; + announcements.clear(); + if(log.isDebugEnabled()) + log.debug("coordinator announcement task stopped"); + } + + + + /** + * Returns a random value within [min_interval - max_interval] + */ + long computeInterval() { + return min_interval + Util.random(max_interval - min_interval); + } + + + + void sendCoordinatorAnnouncement(Address coord) { + Message coord_announcement=new Message(); // multicast to all + CoordAnnouncement hdr=new CoordAnnouncement(coord); + coord_announcement.putHeader(getName(), hdr); + down_prot.down(new Event(Event.MSG, coord_announcement)); + } + + void processAnnouncements() { + if(announcements.size() > 1) { + Vector coords=new Vector(announcements); // create a clone + if(coords.size() > 1) { + if(log.isDebugEnabled()) + log.debug("passing up MERGE event, coords=" + coords); + final Event evt=new Event(Event.MERGE, coords); + if(use_separate_thread) { + Thread merge_notifier=new Thread(Util.getGlobalThreadGroup(), "merge notifier thread") { + public void run() { + up_prot.up(evt); + } + }; + merge_notifier.setDaemon(true); + merge_notifier.start(); + } + else { + up_prot.up(evt); + } + } + announcements.clear(); + announcements.add(local_addr); + } + } + + + class CoordinatorAnnouncer implements TimeScheduler.Task { + public long nextInterval() { + return computeInterval(); + } + + public void run() { + if(is_coord) + sendCoordinatorAnnouncement(local_addr); + } + } + + + + public static class CoordAnnouncement extends Header { + Address coord_addr=null; + + public CoordAnnouncement() { + } + + public CoordAnnouncement(Address coord) { + this.coord_addr=coord; + } + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + coord_addr=(Address)in.readObject(); + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeObject(coord_addr); + } + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/MERGEFAST.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/MERGEFAST.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/MERGEFAST.java 17 Aug 2012 14:51:06 -0000 1.1 @@ -0,0 +1,112 @@ +package org.jgroups.protocols; + +import org.jgroups.*; +import org.jgroups.stack.Protocol; + +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.util.Vector; + +/** + * The coordinator attaches a small header to each (or every nth) message. If another coordinator in the + * same group sees the message, it will initiate the merge protocol immediately by sending a MERGE + * event up the stack. + * @author Bela Ban, Aug 25 2003 + */ +public class MERGEFAST extends Protocol { + Address local_addr=null; + boolean is_coord=false; + static final String name="MERGEFAST"; + + public String getName() { + return name; + } + + + public Object down(Event evt) { + if(is_coord == true && evt.getType() == Event.MSG && local_addr != null) { + Message msg=(Message)evt.getArg(); + Address dest=msg.getDest(); + if(dest == null || dest.isMulticastAddress()) { + msg.putHeader(getName(), new MergefastHeader(local_addr)); + } + } + + if(evt.getType() == Event.VIEW_CHANGE) { + handleViewChange((View)evt.getArg()); + } + + return down_prot.down(evt); + } + + + + public Object up(Event evt) { + switch(evt.getType()) { + case Event.SET_LOCAL_ADDRESS: + local_addr=(Address)evt.getArg(); + break; + case Event.MSG: + if(is_coord == false) // only handle message if we are coordinator + break; + Message msg=(Message)evt.getArg(); + MergefastHeader hdr=(MergefastHeader)msg.getHeader(name); + up_prot.up(evt); + if(hdr != null && local_addr != null) { + Address other_coord=hdr.coord; + if(!local_addr.equals(other_coord)) { + sendUpMerge(new Address[]{local_addr, other_coord}); + } + } + return null; // event was already passed up + case Event.VIEW_CHANGE: + handleViewChange((View)evt.getArg()); + break; + } + return up_prot.up(evt); + } + + + void handleViewChange(View v) { + Vector mbrs; + if(local_addr == null) + return; + mbrs=v.getMembers(); + is_coord=mbrs != null && !mbrs.isEmpty() && local_addr.equals(mbrs.firstElement()); + } + + /** + * @todo avoid sending up too many MERGE events. + */ + void sendUpMerge(Address[] addresses) { + Vector v=new Vector(11); + for(int i=0; i < addresses.length; i++) { + Address addr=addresses[i]; + v.add(addr); + } + up_prot.up(new Event(Event.MERGE, v)); + } + + + public static class MergefastHeader extends Header { + Address coord=null; + + public MergefastHeader() { + } + + public MergefastHeader(Address coord) { + this.coord=coord; + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeObject(coord); + } + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + coord=(Address)in.readObject(); + } + + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/MPING.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/MPING.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/MPING.java 17 Aug 2012 14:51:07 -0000 1.1 @@ -0,0 +1,392 @@ +package org.jgroups.protocols; + +import org.jgroups.Event; +import org.jgroups.Global; +import org.jgroups.Message; +import org.jgroups.util.Buffer; +import org.jgroups.util.ExposedByteArrayOutputStream; +import org.jgroups.util.Util; + +import java.io.*; +import java.net.*; +import java.util.*; + +/** + * Uses its own IP multicast socket to send and receive discovery requests/responses. Can be used in + * conjuntion with a non-UDP transport, e.g. TCP.

        + * The discovery is assymetric: discovery requests are broadcast via the multicast socket, and + * received via the multicast socket by everyone in the group. However, the discovery responses are sent + * back via the regular transport (e.g. TCP) to the sender (discovery request contained sender's regular address, + * e.g. 192.168.0.2:7800). + * @author Bela Ban + * @version $Id: MPING.java,v 1.1 2012/08/17 14:51:07 marcin Exp $ + */ +public class MPING extends PING implements Runnable { + MulticastSocket mcast_sock=null; + + /** If we have multiple mcast send sockets, e.g. send_interfaces or send_on_all_interfaces enabled */ + MulticastSocket[] mcast_send_sockets=null; + Thread receiver=null; + InetAddress bind_addr=null; + int ip_ttl=8; + InetAddress mcast_addr=null; + int mcast_port=7555; + + /** If true, the transport should use all available interfaces to receive multicast messages */ + boolean receive_on_all_interfaces=false; + + /** List of interfaces to receive multicasts on. The multicast receive socket will listen + * on all of these interfaces. This is a comma-separated list of IP addresses or interface names. E.g. + * "192.168.5.1,eth1,127.0.0.1". Duplicates are discarded; we only bind to an interface once. + * If this property is set, it override receive_on_all_interfaces. + */ + List receive_interfaces=null; + + /** If true, the transport should use all available interfaces to send multicast messages. This means + * the same multicast message is sent N times, so use with care */ + boolean send_on_all_interfaces=false; + + /** List of interfaces to send multicasts on. The multicast send socket will send the + * same multicast message on all of these interfaces. This is a comma-separated list of IP addresses or + * interface names. E.g. "192.168.5.1,eth1,127.0.0.1". Duplicates are discarded. + * If this property is set, it override send_on_all_interfaces. + */ + List send_interfaces=null; + + /** Pre-allocated byte stream. Used for serializing datagram packets. Will grow as needed */ + final ExposedByteArrayOutputStream out_stream=new ExposedByteArrayOutputStream(128); + byte receive_buf[]=new byte[1024]; + + + private static final boolean can_bind_to_mcast_addr; // are we running on Linux ? + + + static { + can_bind_to_mcast_addr=Util.checkForLinux() || Util.checkForSolaris(); + } + + + + public String getName() { + return "MPING"; + } + + public InetAddress getBindAddr() { + return bind_addr; + } + + public void setBindAddr(InetAddress bind_addr) { + this.bind_addr=bind_addr; + } + + public List getReceiveInterfaces() { + return receive_interfaces; + } + + public List getSendInterfaces() { + return send_interfaces; + } + + public boolean isReceiveOnAllInterfaces() { + return receive_on_all_interfaces; + } + + public boolean isSendOnAllInterfaces() { + return send_on_all_interfaces; + } + + public int getTTL() { + return ip_ttl; + } + + public void setTTL(int ip_ttl) { + this.ip_ttl=ip_ttl; + } + + public InetAddress getMcastAddr() { + return mcast_addr; + } + + public void setMcastAddr(InetAddress mcast_addr) { + this.mcast_addr=mcast_addr; + } + + public int getMcastPort() { + return mcast_port; + } + + public void setMcastPort(int mcast_port) { + this.mcast_port=mcast_port; + } + + + public boolean setProperties(Properties props) { + String str; + + try { + bind_addr=Util.getBindAddress(props); + } + catch(UnknownHostException unknown) { + log.fatal("failed getting bind_addr", unknown); + return false; + } + catch(SocketException ex) { + log.fatal("failed getting bind_addr", ex); + return false; + } + + str=Util.getProperty(new String[]{Global.MPING_MCAST_ADDR}, props, "mcast_addr", false, "230.5.6.7"); + if(str != null) { + try { + mcast_addr=InetAddress.getByName(str); + } + catch(UnknownHostException e) { + log.error("could not resolve " + str, e); + return false; + } + props.remove("mcast_addr"); + } + + str=Util.getProperty(new String[]{Global.MPING_MCAST_PORT}, props, "mcast_port", false, "7555"); + if(str != null) { + mcast_port=Integer.parseInt(str); + props.remove("mcast_port"); + } + + str=Util.getProperty(new String[]{Global.MPING_IP_TTL}, props, "ip_ttl", false, "16"); + if(str != null) { + ip_ttl=Integer.parseInt(str); + props.remove("ip_ttl"); + } + + str=props.getProperty("bind_to_all_interfaces"); + if(str != null) { + receive_on_all_interfaces=Boolean.parseBoolean(str); + props.remove("bind_to_all_interfaces"); + log.warn("bind_to_all_interfaces has been deprecated; use receive_on_all_interfaces instead"); + props.remove("bind_to_all_interfaces"); + } + + str=props.getProperty("receive_on_all_interfaces"); + if(str != null) { + receive_on_all_interfaces=Boolean.parseBoolean(str); + props.remove("receive_on_all_interfaces"); + } + + str=props.getProperty("receive_interfaces"); + if(str != null) { + try { + receive_interfaces=Util.parseInterfaceList(str); + props.remove("receive_interfaces"); + } + catch(Exception e) { + log.error("error determining interfaces (" + str + ")", e); + return false; + } + } + + str=props.getProperty("send_on_all_interfaces"); + if(str != null) { + send_on_all_interfaces=Boolean.parseBoolean(str); + props.remove("send_on_all_interfaces"); + } + + str=props.getProperty("send_interfaces"); + if(str != null) { + try { + send_interfaces=Util.parseInterfaceList(str); + props.remove("send_interfaces"); + } + catch(Exception e) { + log.error("error determining interfaces (" + str + ")", e); + return false; + } + } + + if(mcast_addr == null) { + try { + mcast_addr=InetAddress.getByName("230.5.6.7"); + } + catch(UnknownHostException e) { + log.error("failed getting default mcast address", e); + return false; + } + } + return super.setProperties(props); + } + + + public Object up(Event evt) { + if(evt.getType() == Event.CONFIG) { + if(bind_addr == null) { + Map config=(Map)evt.getArg(); + bind_addr=(InetAddress)config.get("bind_addr"); + } + return up_prot.up(evt); + } + return super.up(evt); + } + + + public void start() throws Exception { + if(can_bind_to_mcast_addr) // https://jira.jboss.org/jira/browse/JGRP-836 - prevent cross talking on Linux + mcast_sock=Util.createMulticastSocket(mcast_addr, mcast_port, log); + else + mcast_sock=new MulticastSocket(mcast_port); + + mcast_sock.setTimeToLive(ip_ttl); + + if(receive_on_all_interfaces || (receive_interfaces != null && !receive_interfaces.isEmpty())) { + List interfaces; + if(receive_interfaces != null) + interfaces=receive_interfaces; + else + interfaces=Util.getAllAvailableInterfaces(); + bindToInterfaces(interfaces, mcast_sock, mcast_addr); + } + else { + if(bind_addr != null) + mcast_sock.setInterface(bind_addr); + mcast_sock.joinGroup(mcast_addr); + } + + + // 3b. Create mcast sender socket + if(send_on_all_interfaces || (send_interfaces != null && !send_interfaces.isEmpty())) { + List interfaces; + NetworkInterface intf; + if(send_interfaces != null) + interfaces=send_interfaces; + else + interfaces=Util.getAllAvailableInterfaces(); + mcast_send_sockets=new MulticastSocket[interfaces.size()]; + int index=0; + for(Iterator it=interfaces.iterator(); it.hasNext();) { + intf=(NetworkInterface)it.next(); + mcast_send_sockets[index]=new MulticastSocket(); + mcast_send_sockets[index].setNetworkInterface(intf); + mcast_send_sockets[index].setTimeToLive(ip_ttl); + index++; + } + } + + + startReceiver(); + super.start(); + } + + + private void bindToInterfaces(List interfaces, MulticastSocket s, InetAddress mcast_addr) throws IOException { + SocketAddress tmp_mcast_addr=new InetSocketAddress(mcast_addr, mcast_port); + for(Iterator it=interfaces.iterator(); it.hasNext();) { + NetworkInterface i=(NetworkInterface)it.next(); + for(Enumeration en2=i.getInetAddresses(); en2.hasMoreElements();) { + InetAddress addr=(InetAddress)en2.nextElement(); + s.joinGroup(tmp_mcast_addr, i); + if(log.isTraceEnabled()) + log.trace("joined " + tmp_mcast_addr + " on " + i.getName() + " (" + addr + ")"); + break; + } + } + } + + + + private void startReceiver() { + if(receiver == null || !receiver.isAlive()) { + receiver=new Thread(Util.getGlobalThreadGroup(), this, "ReceiverThread"); + receiver.setDaemon(true); + receiver.start(); + if(log.isTraceEnabled()) + log.trace("receiver thread started"); + } + } + + public void stop() { + Util.close(mcast_sock); + mcast_sock=null; + receiver=null; + super.stop(); + } + + void sendMcastDiscoveryRequest(Message msg) { + Buffer buf; + DatagramPacket packet; + DataOutputStream out=null; + + try { + if(msg.getSrc() == null) + msg.setSrc(local_addr); + out_stream.reset(); + out=new DataOutputStream(out_stream); + msg.writeTo(out); + out.flush(); // flushes contents to out_stream + buf=new Buffer(out_stream.getRawBuffer(), 0, out_stream.size()); + packet=new DatagramPacket(buf.getBuf(), buf.getOffset(), buf.getLength(), mcast_addr, mcast_port); + discovery_reception.reset(); + if(mcast_send_sockets != null) { + MulticastSocket s; + for(int i=0; i < mcast_send_sockets.length; i++) { + s=mcast_send_sockets[i]; + try { + s.send(packet); + } + catch(Exception e) { + log.error("failed sending packet on socket " + s); + } + } + } + else { // DEFAULT path + if(mcast_sock != null) + mcast_sock.send(packet); + } + waitForDiscoveryRequestReception(); + } + catch(IOException ex) { + log.error("failed sending discovery request", ex); + } + finally { + Util.close(out); + } + } + + + + public void run() { + DatagramPacket packet=new DatagramPacket(receive_buf, receive_buf.length); + byte[] data; + ByteArrayInputStream inp_stream=null; + DataInputStream inp=null; + Message msg; + + while(mcast_sock != null && receiver != null && Thread.currentThread().equals(receiver)) { + packet.setData(receive_buf, 0, receive_buf.length); + try { + mcast_sock.receive(packet); + data=packet.getData(); + inp_stream=new ByteArrayInputStream(data, 0, data.length); + inp=new DataInputStream(inp_stream); + msg=new Message(); + msg.readFrom(inp); + up(new Event(Event.MSG, msg)); + } + catch(SocketException socketEx) { + break; + } + catch(Throwable ex) { + log.error("failed receiving packet (from " + packet.getSocketAddress() + ")", ex); + } + finally { + closeInputStream(inp); + closeInputStream(inp_stream); + } + } + if(log.isTraceEnabled()) + log.trace("receiver thread terminated"); + } + + private static void closeInputStream(InputStream inp) { + if(inp != null) + try {inp.close();} catch(Throwable e) {} + } +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/PARTITION.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/PARTITION.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/PARTITION.java 17 Aug 2012 14:51:06 -0000 1.1 @@ -0,0 +1,64 @@ + +package org.jgroups.protocols; + +import org.jgroups.Address; +import org.jgroups.Event; +import org.jgroups.Message; +import org.jgroups.stack.Protocol; + + +/** + * Protocol to simulate a partition. This can be done in 2 ways: send down a PARTITION event (with a boolean flag on or + * off, to start or end a partition), or by grabbing a reference to the protocol via the ProtocolStack and calling the + * methods startPartition() or stopPartition() directly. This can also be done via JMX.

        + * A partition simply discards all messages, but let's other events pass. + * @author Bela Ban + * @version $Id: PARTITION.java,v 1.1 2012/08/17 14:51:06 marcin Exp $ + */ +public class PARTITION extends Protocol { + protected boolean partition_on=false; + protected Address local_address=null; + + public String getName() { + return "PARTITION"; + } + + public boolean isPartitionOn() { + return partition_on; + } + + public void startPartition() { + partition_on=true; + } + + public void stopPartition() { + partition_on=false; + } + + + public Object down(Event evt) { + switch(evt.getType()) { + case Event.START_PARTITION: + startPartition(); + return null; + case Event.STOP_PARTITION: + stopPartition(); + return null; + default: + return down_prot.down(evt); + } + } + + public Object up(Event evt) { + if(evt.getType() == Event.SET_LOCAL_ADDRESS) + local_address=(Address)evt.getArg(); + if(partition_on == false) + return up_prot.up(evt); + if(evt.getType() != Event.MSG) + return up_prot.up(evt); + Message msg=(Message)evt.getArg(); + if(msg.getSrc().equals(local_address)) + return up_prot.up(evt); + return null; + } +} \ No newline at end of file Index: 3rdParty_sources/jgroups/org/jgroups/protocols/PERF_TP.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/PERF_TP.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/PERF_TP.java 17 Aug 2012 14:51:07 -0000 1.1 @@ -0,0 +1,158 @@ +// $Id: PERF_TP.java,v 1.1 2012/08/17 14:51:07 marcin Exp $ + +package org.jgroups.protocols; + + +import org.jgroups.Address; +import org.jgroups.Event; +import org.jgroups.Message; +import org.jgroups.stack.Protocol; + + +/** + * Measures the time for a message to travel from the channel to the transport + * @author Bela Ban + * @version $Id: PERF_TP.java,v 1.1 2012/08/17 14:51:07 marcin Exp $ + */ +public class PERF_TP extends Protocol { + private Address local_addr=null; + static volatile PERF_TP instance=null; + long stop, start; + long num_msgs=0; + long expected_msgs=0; + boolean done=false; + + + public static PERF_TP getInstance() { + return instance; + } + + public PERF_TP() { + if(instance == null) + instance=this; + } + + + public String toString() { + return "Protocol PERF_TP (local address: " + local_addr + ')'; + } + + public boolean done() { + return done; + } + + public long getNumMessages() { + return num_msgs; + } + + public void setExpectedMessages(long m) { + expected_msgs=m; + num_msgs=0; + done=false; + start=System.currentTimeMillis(); + } + + public void reset() { + num_msgs=expected_msgs=stop=start=0; + done=false; + } + + public long getTotalTime() { + return stop-start; + } + + + /*------------------------------ Protocol interface ------------------------------ */ + + public String getName() { + return "PERF_TP"; + } + + + + + public void init() throws Exception { + local_addr=new org.jgroups.stack.IpAddress("localhost", 10000); // fake address + } + + public void start() throws Exception { + up_prot.up(new Event(Event.SET_LOCAL_ADDRESS, local_addr)); + } + + + /** + * Caller by the layer above this layer. Usually we just put this Message + * into the send queue and let one or more worker threads handle it. A worker thread + * then removes the Message from the send queue, performs a conversion and adds the + * modified Message to the send queue of the layer below it, by calling Down). + */ + public Object down(Event evt) { + Message msg; + Address dest_addr; + + + switch(evt.getType()) { + + case Event.MSG: + if(done) { + break; + } + msg=(Message)evt.getArg(); + dest_addr=msg.getDest(); + if(dest_addr == null) + num_msgs++; + if(num_msgs >= expected_msgs) { + stop=System.currentTimeMillis(); + synchronized(this) { + done=true; + this.notifyAll(); + } + if(log.isInfoEnabled()) log.info("all done (num_msgs=" + num_msgs + ", expected_msgs=" + expected_msgs); + } + break; + + case Event.CONNECT: + return null; + } + + if(getDownProtocol() != null) + return down_prot.down(evt); + return null; + } + + + public Object up(Event evt) { + Message msg; + Address dest_addr; + switch(evt.getType()) { + + case Event.MSG: + if(done) { + if(log.isWarnEnabled()) log.warn("all done (discarding msg)"); + break; + } + msg=(Message)evt.getArg(); + dest_addr=msg.getDest(); + if(dest_addr == null) + num_msgs++; + if(num_msgs >= expected_msgs) { + stop=System.currentTimeMillis(); + synchronized(this) { + done=true; + this.notifyAll(); + } + if(log.isInfoEnabled()) log.info("all done (num_msgs=" + num_msgs + ", expected_msgs=" + expected_msgs); + } + else { + return up_prot.up(evt); + } + } + return up_prot.up(evt); + } + + /*--------------------------- End of Protocol interface -------------------------- */ + + + + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/PING.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/PING.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/PING.java 17 Aug 2012 14:51:05 -0000 1.1 @@ -0,0 +1,327 @@ + +package org.jgroups.protocols; + +import org.jgroups.*; +import org.jgroups.stack.GossipClient; +import org.jgroups.stack.IpAddress; +import org.jgroups.util.Util; +import org.jgroups.util.Promise; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.*; + + +/** + * The PING protocol layer retrieves the initial membership (used by the GMS when started + * by sending event FIND_INITIAL_MBRS down the stack). We do this by mcasting PING + * requests to an IP MCAST address (or, if gossiping is enabled, by contacting the GossipRouter). + * The responses should allow us to determine the coordinator whom we have to + * contact, e.g. in case we want to join the group. When we are a server (after having + * received the BECOME_SERVER event), we'll respond to PING requests with a PING + * response.

        The FIND_INITIAL_MBRS event will eventually be answered with a + * FIND_INITIAL_MBRS_OK event up the stack. + * The following properties are available + * property: gossip_host - if you are using GOSSIP then this defines the host of the GossipRouter, default is null + * property: gossip_port - if you are using GOSSIP then this defines the port of the GossipRouter, default is null + * @author Bela Ban + * @version $Id: PING.java,v 1.1 2012/08/17 14:51:05 marcin Exp $ + */ +public class PING extends Discovery { + String gossip_host=null; + Vector gossip_hosts=null; + int gossip_port=0; + long gossip_refresh=20000; // time in msecs after which the entry in GossipRouter will be refreshed + GossipClient client; + int port_range=1; // number of ports to be probed for initial membership + private List

        initial_hosts=null; // hosts to be contacted for the initial membership + int sock_conn_timeout=1000; // max time in millis for a socket creation + int sock_read_timeout=3000; // max time in millis for a socket read + protected final Promise discovery_reception=new Promise(); + /** Time (in ms) to wait for our own discovery message to be received. 0 means don't wait. If the + discovery message is not received within discovery_timeout ms, a warning will be logged */ + private long discovery_timeout=0L; + public static final String name="PING"; + + + public String getName() { + return name; + } + + + + /** + * sets the properties of the PING protocol. + * The following properties are available + * property: timeout - the timeout (ms) to wait for the initial members, default is 3000=3 secs + * property: num_initial_members - the minimum number of initial members for a FIND_INITAL_MBRS, default is 2 + * property: gossip_host - if you are using GOSSIP then this defines the host of the GossipRouter, default is null + * property: gossip_port - if you are using GOSSIP then this defines the port of the GossipRouter, default is null + * + * @param props - a property set containing only PING properties + * @return returns true if all properties were parsed properly + * returns false if there are unrecnogized properties in the property set + */ + public boolean setProperties(Properties props) { + String str; + + str=props.getProperty("gossip_host"); + if(str != null) { + gossip_host=str; + props.remove("gossip_host"); + } + + str=props.getProperty("gossip_hosts"); + if(str != null) { + try { + gossip_hosts=createInitialHosts(str); + } + catch(UnknownHostException e) { + throw new IllegalArgumentException(e); + } + props.remove("gossip_hosts"); + } + + str=props.getProperty("gossip_port"); + if(str != null) { + gossip_port=Integer.parseInt(str); + props.remove("gossip_port"); + } + + str=props.getProperty("gossip_refresh"); + if(str != null) { + gossip_refresh=Long.parseLong(str); + props.remove("gossip_refresh"); + } + + str=props.getProperty("sock_conn_timeout"); // wait for at most n members + if(str != null) { + sock_conn_timeout=Integer.parseInt(str); + props.remove("sock_conn_timeout"); + } + + str=props.getProperty("sock_read_timeout"); // wait for at most n members + if(str != null) { + sock_read_timeout=Integer.parseInt(str); + props.remove("sock_read_timeout"); + } + + str=props.getProperty("port_range"); // if member cannot be contacted on base port, + if(str != null) { // how many times can we increment the port + port_range=Integer.parseInt(str); + if(port_range < 1) { + port_range=1; + } + props.remove("port_range"); + } + + str=props.getProperty("discovery_timeout"); // wait for at most n members + if(str != null) { + discovery_timeout=Integer.parseInt(str); + props.remove("discovery_timeout"); + } + + str=props.getProperty("initial_hosts"); + if(str != null) { + props.remove("initial_hosts"); + try { + initial_hosts=new ArrayList
        (); + List tmp=createInitialHosts(str); + initial_hosts.addAll(tmp); + } + catch(UnknownHostException e) { + if(log.isErrorEnabled()) + log.error("failed constructing initial list of hosts", e); + return false; + } + } + + return super.setProperties(props); + } + + public int getGossipPort() { + return gossip_port; + } + + public void setGossipPort(int gossip_port) { + this.gossip_port=gossip_port; + } + + public long getGossipRefresh() { + return gossip_refresh; + } + + public void setGossipRefresh(long gossip_refresh) { + this.gossip_refresh=gossip_refresh; + } + + public void init() throws Exception { + super.init(); + if(gossip_hosts != null) { + client=new GossipClient(gossip_hosts, gossip_refresh); + client.setSocketConnectionTimeout(sock_conn_timeout); + client.setSocketReadTimeout(sock_read_timeout); + } + else if(gossip_host != null && gossip_port != 0) { + client=new GossipClient(new IpAddress(InetAddress.getByName(gossip_host), gossip_port), gossip_refresh); + client.setSocketConnectionTimeout(sock_conn_timeout); + client.setSocketReadTimeout(sock_read_timeout); + } + } + + public void stop() { + super.stop(); + if(client != null) { + client.stop(); + } + discovery_reception.reset(); + } + + + public void localAddressSet(Address addr) { + // Add own address to initial_hosts if not present: we must always be able to ping ourself ! + if(initial_hosts != null && addr != null) { + if(initial_hosts.contains(addr)) { + initial_hosts.remove(addr); + if(log.isDebugEnabled()) log.debug("[SET_LOCAL_ADDRESS]: removing my own address (" + addr + + ") from initial_hosts; initial_hosts=" + initial_hosts); + } + } + } + + + + public void handleConnect() { + if(client != null) + client.register(group_addr, local_addr); + } + + public void handleDisconnect() { + if(client != null) { + if(group_addr != null && local_addr != null) + client.unregister(group_addr, local_addr); + client.stop(); + } + } + + + + public void sendGetMembersRequest(Promise promise) throws Exception { + Message msg; + PingHeader hdr; + List
        gossip_rsps; + + if(client != null) { + gossip_rsps=client.getMembers(group_addr); + if(gossip_rsps != null && !gossip_rsps.isEmpty()) { + // Set a temporary membership in the UDP layer, so that the following multicast + // will be sent to all of them + Event view_event=new Event(Event.TMP_VIEW, makeView(new Vector
        (gossip_rsps))); + down_prot.down(view_event); // needed e.g. by failure detector or UDP + } + else { + //do nothing + return; + } + + if(!gossip_rsps.isEmpty()) { + for(Iterator
        it=gossip_rsps.iterator(); it.hasNext();) { + Address dest=it.next(); + msg=new Message(dest, null, null); // unicast msg + msg.setFlag(Message.OOB); + msg.putHeader(getName(), new PingHeader(PingHeader.GET_MBRS_REQ, null)); + down_prot.down(new Event(Event.MSG, msg)); + } + } + + Util.sleep(500); + } + else { + if(initial_hosts != null && !initial_hosts.isEmpty()) { + for(Address addr: initial_hosts) { + // if(tmpMbrs.contains(addr)) { + // ; // continue; // changed as suggested by Mark Kopec + // } + msg=new Message(addr, null, null); + msg.setFlag(Message.OOB); + msg.putHeader(name, new PingHeader(PingHeader.GET_MBRS_REQ, null)); + + if(log.isTraceEnabled()) log.trace("[FIND_INITIAL_MBRS] sending PING request to " + msg.getDest()); + down_prot.down(new Event(Event.MSG, msg)); + } + } + else { + // 1. Mcast GET_MBRS_REQ message + hdr=new PingHeader(PingHeader.GET_MBRS_REQ, null); + msg=new Message(null); // mcast msg + msg.setFlag(Message.OOB); + msg.putHeader(getName(), hdr); // needs to be getName(), so we might get "MPING" ! + sendMcastDiscoveryRequest(msg); + } + } + } + + public Object up(Event evt) { + if(evt.getType() == Event.MSG) { + Message msg=(Message)evt.getArg(); + PingHeader hdr=(PingHeader)msg.getHeader(getName()); + if(hdr != null && hdr.type == PingHeader.GET_MBRS_REQ && msg.getSrc().equals(local_addr)) { + discovery_reception.setResult(true); + } + } + + return super.up(evt); + } + + void sendMcastDiscoveryRequest(Message discovery_request) { + discovery_reception.reset(); + down_prot.down(new Event(Event.MSG, discovery_request)); + waitForDiscoveryRequestReception(); + } + + /* -------------------------- Private methods ---------------------------- */ + + + protected void waitForDiscoveryRequestReception() { + if(discovery_timeout > 0) { + try { + discovery_reception.getResultWithTimeout(discovery_timeout); + } + catch(TimeoutException e) { + if(log.isWarnEnabled()) + log.warn("didn't receive my own discovery request - multicast socket might not be configured correctly"); + } + } + } + + + /** + * Input is "daddy[8880],sindhu[8880],camille[5555]. Return List of IpAddresses + */ + private Vector createInitialHosts(String l) throws UnknownHostException { + StringTokenizer tok=new StringTokenizer(l, ","); + String t; + IpAddress addr; + Vector retval=new Vector(); + + while(tok.hasMoreTokens()) { + try { + t=tok.nextToken().trim(); + String host=t.substring(0, t.indexOf('[')); + host=host.trim(); + int port=Integer.parseInt(t.substring(t.indexOf('[') + 1, t.indexOf(']'))); + for(int i=port; i < port + port_range; i++) { + addr=new IpAddress(host, i); + retval.add(addr); + } + } + catch(NumberFormatException e) { + if(log.isErrorEnabled()) log.error("exeption is " + e); + } + } + + return retval; + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/PingHeader.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/PingHeader.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/PingHeader.java 17 Aug 2012 14:51:06 -0000 1.1 @@ -0,0 +1,72 @@ +// $Id: PingHeader.java,v 1.1 2012/08/17 14:51:06 marcin Exp $ + +package org.jgroups.protocols; + +import org.jgroups.Header; +import org.jgroups.Global; +import org.jgroups.util.Streamable; +import org.jgroups.util.Util; + +import java.io.*; + + +public class PingHeader extends Header implements Streamable { + public static final byte GET_MBRS_REQ=1; // arg = null + public static final byte GET_MBRS_RSP=2; // arg = PingRsp(local_addr, coord_addr) + + public byte type=0; + public PingRsp arg=null; + + public PingHeader() { + } // for externalization + + public PingHeader(byte type, PingRsp arg) { + this.type=type; + this.arg=arg; + } + + public int size() { + int retval=Global.BYTE_SIZE *2; // type and presence + if(arg != null) { + retval+=arg.size(); + } + return retval; + } + + public String toString() { + return "[PING: type=" + type2Str(type) + ", arg=" + arg + ']'; + } + + String type2Str(byte t) { + switch(t) { + case GET_MBRS_REQ: + return "GET_MBRS_REQ"; + case GET_MBRS_RSP: + return "GET_MBRS_RSP"; + default: + return ""; + } + } + + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeByte(type); + out.writeObject(arg); + } + + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + type=in.readByte(); + arg=(PingRsp)in.readObject(); + } + + public void writeTo(DataOutputStream outstream) throws IOException { + outstream.writeByte(type); + Util.writeStreamable(arg, outstream); + } + + public void readFrom(DataInputStream instream) throws IOException, IllegalAccessException, InstantiationException { + type=instream.readByte(); + arg=(PingRsp)Util.readStreamable(PingRsp.class, instream); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/PingRsp.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/PingRsp.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/PingRsp.java 17 Aug 2012 14:51:06 -0000 1.1 @@ -0,0 +1,95 @@ +// $Id: PingRsp.java,v 1.1 2012/08/17 14:51:06 marcin Exp $ + +package org.jgroups.protocols; + +import org.jgroups.Address; +import org.jgroups.Global; +import org.jgroups.util.Streamable; +import org.jgroups.util.Util; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.Serializable; + + +public class PingRsp implements Serializable, Streamable { + public Address own_addr=null; + public Address coord_addr=null; + public boolean is_server=false; + private static final long serialVersionUID=3634334590904551586L; + + public PingRsp() { + // externalization + } + + public PingRsp(Address own_addr, Address coord_addr, boolean is_server) { + this.own_addr=own_addr; + this.coord_addr=coord_addr; + this.is_server=is_server; + } + + public boolean equals(Object obj) { + if(!(obj instanceof PingRsp)) + return false; + PingRsp other=(PingRsp)obj; + return own_addr != null && other.own_addr != null && own_addr.equals(other.own_addr); + } + + public int hashCode() { + int retval=0; + if(own_addr != null) + retval+=own_addr.hashCode(); + if(coord_addr != null) + retval+=coord_addr.hashCode(); + if(retval == 0) + retval=super.hashCode(); + return retval; + } + + public boolean isCoord() { + return is_server && own_addr != null && coord_addr != null && own_addr.equals(coord_addr); + } + + public int size() { + int retval=Global.BYTE_SIZE *3; // for is_server, plus 2 presence bytes + if(own_addr != null) { + retval+=Global.BYTE_SIZE; // 1 boolean for: IpAddress or other address ? + retval+=own_addr.size(); + } + if(coord_addr != null) { + retval+=Global.BYTE_SIZE; // 1 boolean for: IpAddress or other address ? + retval+=coord_addr.size(); + } + return retval; + } + + public Address getAddress() { + return own_addr; + } + + public Address getCoordAddress() { + return coord_addr; + } + + public boolean isServer() { + return is_server; + } + + public String toString() { + return new StringBuilder("[own_addr=").append(own_addr).append(", coord_addr=").append(coord_addr). + append(", is_server=").append(is_server).append(']').toString(); + } + + public void writeTo(DataOutputStream outstream) throws IOException { + Util.writeAddress(own_addr, outstream); + Util.writeAddress(coord_addr, outstream); + outstream.writeBoolean(is_server); + } + + public void readFrom(DataInputStream instream) throws IOException, IllegalAccessException, InstantiationException { + own_addr=Util.readAddress(instream); + coord_addr=Util.readAddress(instream); + is_server=instream.readBoolean(); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/SEQUENCER.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/SEQUENCER.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/SEQUENCER.java 17 Aug 2012 14:51:05 -0000 1.1 @@ -0,0 +1,325 @@ + +package org.jgroups.protocols; + +import org.jgroups.*; +import org.jgroups.stack.Protocol; +import org.jgroups.util.SeqnoTable; +import org.jgroups.util.Streamable; +import org.jgroups.util.Util; + +import java.io.*; +import java.util.*; + + +/** + * Implementation of total order protocol using a sequencer. Consult doc/design/SEQUENCER.txt for details + * @author Bela Ban + * @version $Id: SEQUENCER.java,v 1.1 2012/08/17 14:51:05 marcin Exp $ + */ +public class SEQUENCER extends Protocol { + private Address local_addr=null, coord=null; + static final String name="SEQUENCER"; + private boolean is_coord=false; + private long seqno=0; + + /** Map: maintains messages forwarded to the coord which which no ack has been received yet */ + private final Map forward_table=new TreeMap(); + + /** Map: maintains the highest seqnos seen for a given member */ + private final SeqnoTable received_table=new SeqnoTable(0); + + private long forwarded_msgs=0; + private long bcast_msgs=0; + private long received_forwards=0; + private long received_bcasts=0; + + public boolean isCoordinator() {return is_coord;} + public Address getCoordinator() {return coord;} + public Address getLocalAddress() {return local_addr;} + public String getName() {return name;} + public long getForwarded() {return forwarded_msgs;} + public long getBroadcast() {return bcast_msgs;} + public long getReceivedForwards() {return received_forwards;} + public long getReceivedBroadcasts() {return received_bcasts;} + + public void resetStats() { + forwarded_msgs=bcast_msgs=received_forwards=received_bcasts=0L; + } + + public Map dumpStats() { + Map m=super.dumpStats(); + if(m == null) + m=new HashMap(); + m.put("forwarded", new Long(forwarded_msgs)); + m.put("broadcast", new Long(bcast_msgs)); + m.put("received_forwards", new Long(received_forwards)); + m.put("received_bcasts", new Long(received_bcasts)); + return m; + } + + public String printStats() { + return dumpStats().toString(); + } + + + public boolean setProperties(Properties props) { + super.setProperties(props); + + if(!props.isEmpty()) { + log.error("the following properties are not recognized: " + props); + return false; + } + return true; + } + + private final long nextSeqno() { + synchronized(this) { + return seqno++; + } + } + + + public Object down(Event evt) { + switch(evt.getType()) { + case Event.MSG: + Message msg=(Message)evt.getArg(); + Address dest=msg.getDest(); + if(dest == null || dest.isMulticastAddress()) { // only handle multicasts + long next_seqno=nextSeqno(); + SequencerHeader hdr=new SequencerHeader(SequencerHeader.FORWARD, local_addr, next_seqno); + msg.putHeader(name, hdr); + if(!is_coord) { + forwardToCoord(msg, next_seqno); + } + else { + broadcast(msg); + } + return null; // don't pass down + } + break; + + case Event.VIEW_CHANGE: + handleViewChange((View)evt.getArg()); + break; + } + return down_prot.down(evt); + } + + + + + public Object up(Event evt) { + Message msg; + SequencerHeader hdr; + + switch(evt.getType()) { + + case Event.SET_LOCAL_ADDRESS: + local_addr=(Address)evt.getArg(); + break; + + case Event.MSG: + msg=(Message)evt.getArg(); + hdr=(SequencerHeader)msg.getHeader(name); + if(hdr == null) + break; // pass up + + switch(hdr.type) { + case SequencerHeader.FORWARD: + if(!is_coord) { + if(log.isErrorEnabled()) + log.warn("I (" + local_addr + ") am not the coord and don't handle " + + "FORWARD requests, ignoring request"); + return null; + } + broadcast(msg); + received_forwards++; + return null; + case SequencerHeader.BCAST: + deliver(msg, hdr); // deliver a copy and return (discard the original msg) + received_bcasts++; + return null; + } + break; + + case Event.VIEW_CHANGE: + handleViewChange((View)evt.getArg()); + break; + } + + return up_prot.up(evt); + } + + + + /* --------------------------------- Private Methods ----------------------------------- */ + + private void handleViewChange(View v) { + Vector
        members=v.getMembers(); + if(members.isEmpty()) return; + + Address prev_coord=coord; + coord=(Address)members.firstElement(); + is_coord=local_addr != null && local_addr.equals(coord); + + boolean coord_changed=prev_coord != null && !prev_coord.equals(coord); + if(coord_changed) { + resendMessagesInForwardTable(); // maybe optimize in the future: broadcast directly if coord + } + // remove left members from received_table + received_table.retainAll(members); + } + + /** + * Sends all messages currently in forward_table to the new coordinator (changing the dest field). + * This needs to be done, so the underlying reliable unicast protocol (e.g. UNICAST) adds these messages + * to its retransmission mechanism
        + * Note that we need to resend the messages in order of their seqnos ! We also need to prevent other message + * from being inserted until we're done, that's why there's synchronization. + */ + private void resendMessagesInForwardTable() { + Map copy; + synchronized(forward_table) { + copy=new TreeMap(forward_table); + } + for(Message msg: copy.values()) { + msg.setDest(coord); + down_prot.down(new Event(Event.MSG, msg)); + } + } + + + private void forwardToCoord(Message msg, long seqno) { + msg.setDest(coord); // we change the message dest from multicast to unicast (to coord) + synchronized(forward_table) { + forward_table.put(new Long(seqno), msg); + } + down_prot.down(new Event(Event.MSG, msg)); + forwarded_msgs++; + } + + private void broadcast(Message msg) { + SequencerHeader hdr=(SequencerHeader)msg.getHeader(name); + hdr.type=SequencerHeader.BCAST; // we change the type of header, but leave the tag intact + msg.setDest(null); // mcast + msg.setSrc(local_addr); // the coord is sending it - this will be replaced with sender in deliver() + down_prot.down(new Event(Event.MSG, msg)); + bcast_msgs++; + } + + /** + * We copy the message in order to change the sender's address. If we did this on the original message, + * retransmission would likely run into problems, and possibly also stability (STABLE) of messages + * @param msg + * @param hdr + */ + private void deliver(Message msg, SequencerHeader hdr) { + Address original_sender=hdr.getOriginalSender(); + if(original_sender == null) { + if(log.isErrorEnabled()) + log.error("original sender is null, cannot swap sender address back to original sender"); + return; + } + long msg_seqno=hdr.getSeqno(); + + // this is the ack for the message sent by myself + if(original_sender.equals(local_addr)) { + synchronized(forward_table) { + forward_table.remove(new Long(msg_seqno)); + } + } + + // if msg was already delivered, discard it + boolean added=received_table.add(original_sender, msg_seqno); + if(!added) { + if(log.isWarnEnabled()) + log.warn("seqno (" + original_sender + "::" + msg_seqno + " has already been received " + + "(highest received=" + received_table.getHighestReceived(original_sender) + + "); discarding duplicate message"); + return; + } + + // pass a copy of the message up the stack + Message tmp=msg.copy(true); + tmp.setSrc(original_sender); + up_prot.up(new Event(Event.MSG, tmp)); + } + + /* ----------------------------- End of Private Methods -------------------------------- */ + + + + + + public static class SequencerHeader extends Header implements Streamable { + static final byte FORWARD = 1; + static final byte BCAST = 2; + + byte type=-1; + /** the original sender's address and a seqno */ + ViewId tag=null; + + + public SequencerHeader() { + } + + public SequencerHeader(byte type, Address original_sender, long seqno) { + this.type=type; + this.tag=new ViewId(original_sender, seqno); + } + + public Address getOriginalSender() { + return tag != null? tag.getCoordAddress() : null; + } + + public long getSeqno() { + return tag != null? tag.getId() : -1; + } + + public String toString() { + StringBuilder sb=new StringBuilder(64); + sb.append(printType()); + if(tag != null) + sb.append(" (tag=").append(tag).append(")"); + return sb.toString(); + } + + private final String printType() { + switch(type) { + case FORWARD: return "FORWARD"; + case BCAST: return "BCAST"; + default: return "n/a"; + } + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeByte(type); + out.writeObject(tag); + } + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + type=in.readByte(); + tag=(ViewId)in.readObject(); + } + + public void writeTo(DataOutputStream out) throws IOException { + out.writeByte(type); + Util.writeStreamable(tag, out); + } + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + type=in.readByte(); + tag=(ViewId)Util.readStreamable(ViewId.class, in); + } + + public int size() { + int size=Global.BYTE_SIZE *2; // type + presence byte + if(tag != null) + size+=tag.serializedSize(); + return size; + } + + } + + +} \ No newline at end of file Index: 3rdParty_sources/jgroups/org/jgroups/protocols/SFC.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/SFC.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/SFC.java 17 Aug 2012 14:51:08 -0000 1.1 @@ -0,0 +1,600 @@ +package org.jgroups.protocols; + +import org.jgroups.*; +import org.jgroups.annotations.GuardedBy; +import org.jgroups.stack.Protocol; +import org.jgroups.util.Util; +import org.jgroups.util.Streamable; +import org.jgroups.util.BoundedList; + +import java.util.*; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.TimeUnit; +import java.io.*; + +/** + * Simple flow control protocol. After max_credits bytes sent to the group (or an individual member), the sender blocks + * until it receives an ack from all members that they indeed received max_credits bytes. + * Design in doc/design/SimpleFlowControl.txt
        + * Note that SFC supports only flow control for multicast messages; unicast flow control is not supported ! Use FC if + * unicast flow control is required. + * @author Bela Ban + * @version $Id: SFC.java,v 1.1 2012/08/17 14:51:08 marcin Exp $ + */ +public class SFC extends Protocol { + static final String name="SFC"; + + /** Max number of bytes to send per receiver until an ack must be received before continuing sending */ + private long max_credits=2000000; + + private Long MAX_CREDITS; + + private static final Long ZERO_CREDITS=new Long(0); + + /** Current number of credits available to send */ + @GuardedBy("lock") + private long curr_credits_available; + + /** Map which keeps track of bytes received from senders */ + @GuardedBy("received_lock") + private final Map received=new HashMap(12); + + /** Set of members which have requested credits but from whom we have not yet received max_credits bytes */ + @GuardedBy("received_lock") + private final Set
        pending_requesters=new HashSet
        (); + + /** Set of members from whom we haven't yet received credits */ + @GuardedBy("lock") + private final Set
        pending_creditors=new HashSet
        (); + + + private final Lock lock=new ReentrantLock(); + /** Lock protecting access to received and pending_requesters */ + private final Lock received_lock=new ReentrantLock(); + + + /** Used to wait for and signal when credits become available again */ + private final Condition credits_available=lock.newCondition(); + + /** Number of milliseconds after which we send a new credit request if we are waiting for credit responses */ + private long max_block_time=5000; + + /** Last time a thread woke up from blocking and had to request credit */ + private long last_blocked_request=0L; + + private final List
        members=new LinkedList
        (); + + private boolean running=true; + + private boolean frag_size_received=false; + + @GuardedBy("lock") long start, stop; + + + + // ---------------------- Management information ----------------------- + long num_blockings=0; + long num_bytes_sent=0; + long num_credit_requests_sent=0; + long num_credit_requests_received=0; + long num_replenishments_received=0; + long num_replenishments_sent=0; + long total_block_time=0; + + final BoundedList blockings=new BoundedList(50); + + + public void resetStats() { + super.resetStats(); + num_blockings=total_block_time=num_replenishments_received=num_credit_requests_sent=num_bytes_sent=0; + num_replenishments_sent=num_credit_requests_received=0; + blockings.clear(); + } + + public long getMaxCredits() {return max_credits;} + public long getCredits() {return curr_credits_available;} + public long getBytesSent() {return num_bytes_sent;} + public long getBlockings() {return num_blockings;} + public long getCreditRequestsSent() {return num_credit_requests_sent;} + public long getCreditRequestsReceived() {return num_credit_requests_received;} + public long getReplenishmentsReceived() {return num_replenishments_received;} + public long getReplenishmentsSent() {return num_replenishments_sent;} + public long getTotalBlockingTime() {return total_block_time;} + public double getAverageBlockingTime() {return num_blockings == 0? 0 : total_block_time / num_blockings;} + + + public Map dumpStats() { + Map retval=super.dumpStats(); + if(retval == null) + retval=new HashMap(); + return retval; + } + + public String printBlockingTimes() { + return blockings.toString(); + } + + public String printReceived() { + received_lock.lock(); + try { + return received.toString(); + } + finally { + received_lock.unlock(); + } + } + + public String printPendingCreditors() { + lock.lock(); + try { + return pending_creditors.toString(); + } + finally { + lock.unlock(); + } + } + + public String printPendingRequesters() { + received_lock.lock(); + try { + return pending_requesters.toString(); + } + finally { + received_lock.unlock(); + } + } + + public void unblock() { + lock.lock(); + try { + curr_credits_available=max_credits; + credits_available.signalAll(); + } + finally { + lock.unlock(); + } + } + + // ------------------- End of management information ---------------------- + + + public final String getName() { + return name; + } + + public boolean setProperties(Properties props) { + String str; + super.setProperties(props); + + str=props.getProperty("max_block_time"); + if(str != null) { + max_block_time=Long.parseLong(str); + props.remove("max_block_time"); + } + + str=props.getProperty("max_credits"); + if(str != null) { + max_credits=Long.parseLong(str); + props.remove("max_credits"); + } + + Util.checkBufferSize("SFC.max_credits", max_credits); + MAX_CREDITS=new Long(max_credits); + curr_credits_available=max_credits; + + if(!props.isEmpty()) { + log.error("the following properties are not recognized: " + props); + return false; + } + return true; + } + + + + public Object down(Event evt) { + switch(evt.getType()) { + case Event.MSG: + Message msg=(Message)evt.getArg(); + Address dest=msg.getDest(); + if(dest != null && !dest.isMulticastAddress()) // only handle multicast messages + break; + + boolean send_credit_request=false; + lock.lock(); + try { + while(curr_credits_available <=0 && running) { + if(log.isTraceEnabled()) + log.trace("blocking (current credits=" + curr_credits_available + ")"); + try { + num_blockings++; + // will be signalled when we have credit responses from all members + boolean rc=credits_available.await(max_block_time, TimeUnit.MILLISECONDS); + if(rc || (curr_credits_available <=0 && running)) { + if(log.isTraceEnabled()) + log.trace("returned from await but credits still unavailable (credits=" +curr_credits_available +")"); + long now=System.currentTimeMillis(); + if(now - last_blocked_request >= max_block_time) { + last_blocked_request=now; + lock.unlock(); // send the credit request without holding the lock + try { + sendCreditRequest(true); + } + finally { + lock.lock(); // now acquire the lock again + } + } + } + else { + // reset the last_blocked_request stamp so the + // next timed out block will for sure send a request + last_blocked_request=0; + } + } + catch(InterruptedException e) { + // bela June 16 2007: http://jira.jboss.com/jira/browse/JGRP-536 +// if(log.isWarnEnabled()) +// log.warn("thread was interrupted", e); +// Thread.currentThread().interrupt(); // pass the exception on to the caller +// return null; + } + } + + // when we get here, curr_credits_available is guaranteed to be > 0 + int len=msg.getLength(); + num_bytes_sent+=len; + curr_credits_available-=len; // we'll block on insufficient credits on the next down() call + if(curr_credits_available <=0) { + pending_creditors.clear(); + synchronized(members) { + pending_creditors.addAll(members); + } + send_credit_request=true; + } + } + finally { + lock.unlock(); + } + + // we don't need to protect send_credit_request because a thread above either (a) decrements the credits + // by the msg length and sets send_credit_request to true or (b) blocks because there are no credits + // available. So only 1 thread can ever set send_credit_request at any given time + if(send_credit_request) { + if(log.isTraceEnabled()) + log.trace("sending credit request to group"); + start=System.nanoTime(); // only 1 thread is here at any given time + Object ret=down_prot.down(evt); // send the message before the credit request + sendCreditRequest(false); // do this outside of the lock + return ret; + } + break; + + case Event.VIEW_CHANGE: + handleViewChange((View)evt.getArg()); + break; + + case Event.SUSPECT: + handleSuspect((Address)evt.getArg()); + break; + + case Event.INFO: + Map map=(Map)evt.getArg(); + handleInfo(map); + break; + } + + return down_prot.down(evt); + } + + + + public Object up(Event evt) { + switch(evt.getType()) { + + case Event.MSG: + Message msg=(Message)evt.getArg(); + Header hdr=(Header)msg.getHeader(name); + Address sender=msg.getSrc(); + if(hdr != null) { + switch(hdr.type) { + case Header.CREDIT_REQUEST: + handleCreditRequest(sender, false); + break; + case Header.URGENT_CREDIT_REQUEST: + handleCreditRequest(sender, true); + break; + case Header.REPLENISH: + handleCreditResponse(sender); + break; + default: + if(log.isErrorEnabled()) + log.error("unknown header type " + hdr.type); + break; + } + return null; // we don't pass the request further up + } + + Address dest=msg.getDest(); + if(dest != null && !dest.isMulticastAddress()) // we don't handle unicast messages + break; + + handleMessage(msg, sender); + break; + + case Event.VIEW_CHANGE: + handleViewChange((View)evt.getArg()); + break; + + case Event.SUSPECT: + handleSuspect((Address)evt.getArg()); + break; + case Event.INFO: + Map map=(Map)evt.getArg(); + handleInfo(map); + break; + } + return up_prot.up(evt); + } + + + + + public void start() throws Exception { + super.start(); + if(!frag_size_received) { + log.warn("No fragmentation protocol was found. When flow control (e.g. FC or SFC) is used, we recommend " + + "a fragmentation protocol, due to http://jira.jboss.com/jira/browse/JGRP-590"); + } + running=true; + } + + + public void stop() { + super.stop(); + running=false; + lock.lock(); + try { + credits_available.signalAll(); + } + finally { + lock.unlock(); + } + } + + + private void handleInfo(Map map) { + if(map != null) { + Integer frag_size=(Integer)map.get("frag_size"); + if(frag_size != null) { + if(frag_size > max_credits) { + log.warn("The fragmentation size of the fragmentation protocol is " + frag_size + + ", which is greater than the max credits. While this is not incorrect, " + + "it may lead to long blockings. Frag size should be less than max_credits " + + "(http://jira.jboss.com/jira/browse/JGRP-590)"); + } + frag_size_received=true; + } + } + } + + private void handleMessage(Message msg, Address sender) { + int len=msg.getLength(); // we don't care about headers, this is faster than size() + + Long new_val; + boolean send_credit_response=false; + + received_lock.lock(); + try { + Long credits=received.get(sender); + if(credits == null) { + new_val=MAX_CREDITS; + received.put(sender, new_val); + } + else { + new_val=credits.longValue() + len; + received.put(sender, new_val); + } + // if(log.isTraceEnabled()) + // log.trace("received " + len + " bytes from " + sender + ": total=" + new_val + " bytes"); + + // see whether we have any pending credit requests + if(!pending_requesters.isEmpty() + && pending_requesters.contains(sender) + && new_val.longValue() >= max_credits) { + pending_requesters.remove(sender); + if(log.isTraceEnabled()) + log.trace("removed " + sender + " from credit requesters; sending credits"); + received.put(sender, ZERO_CREDITS); + send_credit_response=true; + } + } + finally { + received_lock.unlock(); + } + + if(send_credit_response) // send outside of the monitor + sendCreditResponse(sender); + } + + + private void handleCreditRequest(Address sender, boolean urgent) { + boolean send_credit_response=false; + + received_lock.lock(); + try { + num_credit_requests_received++; + Long bytes=received.get(sender); + if(log.isTraceEnabled()) + log.trace("received credit request from " + sender + " (total received: " + bytes + " bytes"); + + if(bytes == null) { + if(log.isErrorEnabled()) + log.error("received credit request from " + sender + ", but sender is not in received hashmap;" + + " adding it"); + send_credit_response=true; + } + else { + if(bytes.longValue() < max_credits && !urgent) { + if(log.isTraceEnabled()) + log.trace("adding " + sender + " to pending credit requesters"); + pending_requesters.add(sender); + } + else { + send_credit_response=true; + } + } + if(send_credit_response) + received.put(sender, ZERO_CREDITS); + } + finally{ + received_lock.unlock(); + } + + if(send_credit_response) { + sendCreditResponse(sender); + } + } + + private void handleCreditResponse(Address sender) { + lock.lock(); + try { + num_replenishments_received++; + if(pending_creditors.remove(sender) && pending_creditors.isEmpty()) { + curr_credits_available=max_credits; + stop=System.nanoTime(); + long diff=(stop-start)/1000000L; + if(log.isTraceEnabled()) + log.trace("replenished credits to " + curr_credits_available + + " (total blocking time=" + diff + " ms)"); + blockings.add(new Long(diff)); + total_block_time+=diff; + credits_available.signalAll(); + } + } + finally{ + lock.unlock(); + } + } + + + + private void handleViewChange(View view) { + List
        mbrs=view != null? view.getMembers() : null; + if(mbrs != null) { + synchronized(members) { + members.clear(); + members.addAll(mbrs); + } + } + + lock.lock(); + try { + // remove all members which left from pending_creditors + if(pending_creditors.retainAll(members) && pending_creditors.isEmpty()) { + // the collection was changed and is empty now as a result of retainAll() + curr_credits_available=max_credits; + if(log.isTraceEnabled()) + log.trace("replenished credits to " + curr_credits_available); + credits_available.signalAll(); + } + } + finally { + lock.unlock(); + } + + received_lock.lock(); + try { + // remove left members + received.keySet().retainAll(members); + + // add new members with *full* credits (see doc/design/SimpleFlowControl.txt for reason) + for(Address mbr: members) { + if(!received.containsKey(mbr)) + received.put(mbr, MAX_CREDITS); + } + + // remove left members from pending credit requesters + pending_requesters.retainAll(members); + } + finally{ + received_lock.unlock(); + } + } + + + private void handleSuspect(Address suspected_mbr) { + // this is the same as a credit response - we cannot block forever for a crashed member + handleCreditResponse(suspected_mbr); + } + + + private void sendCreditRequest(boolean urgent) { + Message credit_req=new Message(); + // credit_req.setFlag(Message.OOB); // we need to receive the credit request after regular messages + byte type=urgent? Header.URGENT_CREDIT_REQUEST : Header.CREDIT_REQUEST; + credit_req.putHeader(name, new Header(type)); + num_credit_requests_sent++; + down_prot.down(new Event(Event.MSG, credit_req)); + } + + private void sendCreditResponse(Address dest) { + Message credit_rsp=new Message(dest); + credit_rsp.setFlag(Message.OOB); + Header hdr=new Header(Header.REPLENISH); + credit_rsp.putHeader(name, hdr); + if(log.isTraceEnabled()) + log.trace("sending credit response to " + dest); + num_replenishments_sent++; + down_prot.down(new Event(Event.MSG, credit_rsp)); + } + + + + public static class Header extends org.jgroups.Header implements Streamable { + public static final byte CREDIT_REQUEST = 1; // the sender of the message is the requester + public static final byte REPLENISH = 2; // the sender of the message is the creditor + public static final byte URGENT_CREDIT_REQUEST = 3; + + byte type=CREDIT_REQUEST; + + public Header() { + + } + + public Header(byte type) { + this.type=type; + } + + public int size() { + return Global.BYTE_SIZE; + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeByte(type); + } + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + type=in.readByte(); + } + + public void writeTo(DataOutputStream out) throws IOException { + out.writeByte(type); + } + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + type=in.readByte(); + } + + public String toString() { + switch(type) { + case REPLENISH: return "REPLENISH"; + case CREDIT_REQUEST: return "CREDIT_REQUEST"; + case URGENT_CREDIT_REQUEST: return "URGENT_CREDIT_REQUEST"; + default: return ""; + } + } + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/SHARED_LOOPBACK.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/SHARED_LOOPBACK.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/SHARED_LOOPBACK.java 17 Aug 2012 14:51:07 -0000 1.1 @@ -0,0 +1,147 @@ +package org.jgroups.protocols; + +import org.jgroups.Address; +import org.jgroups.Event; +import org.jgroups.Message; +import org.jgroups.stack.IpAddress; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + + +/** + * Loopback transport shared by all channels within the same VM. Property for testing is that no messages are lost. Allows + * us to test various protocols (with ProtocolTester) at maximum speed. + * @author Bela Ban + * @version $Id: SHARED_LOOPBACK.java,v 1.1 2012/08/17 14:51:07 marcin Exp $ + */ +public class SHARED_LOOPBACK extends TP { + private static int next_port=10000; + + /** Map of cluster names and address-protocol mappings. Used for routing messages to all or single members */ + private static final Map> routing_table=new ConcurrentHashMap>(); + + + public SHARED_LOOPBACK() { + } + + + + public String toString() { + return "SHARED_LOOPBACK(local address: " + local_addr + ')'; + } + + public void sendToAllMembers(byte[] data, int offset, int length) throws Exception { + Map dests=routing_table.get(channel_name); + if(dests == null) { + if(log.isWarnEnabled()) + log.warn("no destination found for " + channel_name); + return; + } + for(Map.Entry entry: dests.entrySet()) { + Address dest=entry.getKey(); + SHARED_LOOPBACK target=entry.getValue(); + try { + target.receive(dest, local_addr, data, offset, length); + } + catch(Throwable t) { + log.error("failed sending message to " + dest, t); + } + } + } + + public void sendToSingleMember(Address dest, byte[] data, int offset, int length) throws Exception { + Map dests=routing_table.get(channel_name); + if(dests == null) { + if(log.isWarnEnabled()) + log.warn("no destination found for " + channel_name); + return; + } + SHARED_LOOPBACK target=dests.get(dest); + if(target == null) { + if(log.isWarnEnabled()) + log.warn("destination address " + dest + " not found"); + return; + } + target.receive(dest, local_addr, data, offset, length); + } + + public String getInfo() { + return toString(); + } + + public void postUnmarshalling(Message msg, Address dest, Address src, boolean multicast) { + msg.setDest(dest); + } + + public void postUnmarshallingList(Message msg, Address dest, boolean multicast) { + msg.setDest(dest); + } + + /*------------------------------ Protocol interface ------------------------------ */ + + public String getName() { + return "SHARED_LOOPBACK"; + } + +// public boolean setProperties(Properties props) { +// super.setProperties(props); +// if(!props.isEmpty()) { +// log.error("the following properties are not recognized: " + props); +// return false; +// } +// return true; +// } + + + public void init() throws Exception { + local_addr=new IpAddress("127.0.0.1", next_port++); + super.init(); + } + + public void start() throws Exception { + super.start(); + } + + public void stop() { + super.stop(); + } + + + public Object down(Event evt) { + Object retval=super.down(evt); + + switch(evt.getType()) { + case Event.CONNECT: + case Event.CONNECT_WITH_STATE_TRANSFER: + register(channel_name, local_addr, this); + break; + + case Event.DISCONNECT: + unregister(channel_name, local_addr); + break; + } + + return retval; + } + + private void register(String channel_name, Address local_addr, SHARED_LOOPBACK shared_loopback) { + Map map=routing_table.get(channel_name); + if(map == null) { + map=new ConcurrentHashMap(); + routing_table.put(channel_name, map); + } + map.put(local_addr, shared_loopback); + } + + private void unregister(String channel_name, Address local_addr) { + Map map=routing_table.get(channel_name); + if(map != null) { + map.remove(local_addr); + if(map.isEmpty()) { + routing_table.remove(channel_name); + } + } + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/SHUFFLE.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/SHUFFLE.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/SHUFFLE.java 17 Aug 2012 14:51:06 -0000 1.1 @@ -0,0 +1,129 @@ +package org.jgroups.protocols; + +import org.jgroups.Event; +import org.jgroups.Message; +import org.jgroups.stack.Protocol; + +import java.util.*; + + + +/** + * This layer shuffles upcoming messages, put it just above your bottom layer. + * If you system sends less than 2 messages per sec you can notice a latency due + * to this layer. + * + * @author Gianluca Collot + * + */ + +public class SHUFFLE extends Protocol implements Runnable { + + String name="SHUFFLE"; + final List messages; + Thread messagesHandler; + + public SHUFFLE() { + messages = Collections.synchronizedList(new ArrayList()); + } + + public String getName() { + return name; + } + + public boolean setProperties(Properties props) { + String str; + + super.setProperties(props); + str=props.getProperty("name"); + if(str != null) { + name=str; + props.remove("name"); + } + + if(!props.isEmpty()) { + log.error("DUMMY.setProperties(): these properties are not recognized: " + props); + + return false; + } + return true; + } + + /** + * Adds upcoming messages to the messages List<\code> where the messagesHandler<\code> + * retrieves them. + */ + + public Object up(Event evt) { + Message msg; + + switch (evt.getType()) { + + case Event.MSG: + msg=(Message)evt.getArg(); + // Do something with the event, e.g. extract the message and remove a header. + // Optionally pass up + messages.add(msg); + return null; + } + + return up_prot.up(evt); // Pass up to the layer above us + } + + + + + /** + * Starts the messagesHandler<\code> + */ + public void start() throws Exception { + messagesHandler = new Thread(this,"MessagesHandler"); + messagesHandler.setDaemon(true); + messagesHandler.start(); + } + + /** + * Stops the messagesHandler + */ + public void stop() { + Thread tmp = messagesHandler; + messagesHandler = null; + try { + tmp.join(); + } catch (Exception ex) {ex.printStackTrace();} + } + + /** + * Removes a random chosen message from the messages List<\code> if there + * are less than 10 messages in the List it waits some time to ensure to chose from + * a set of messages > 1. + */ + + public void run() { + Message msg; + while (messagesHandler != null) { + if (!messages.isEmpty()) { + msg = (Message) messages.remove(rnd(messages.size())); + up_prot.up(new Event(Event.MSG,msg)); + } + if (messages.size() < 5) { + try { + Thread.sleep(300); /** @todo make this time user configurable */ + } + catch (Exception ex) { + ex.printStackTrace(); + } + } + }// while + // PassUp remaining messages + Iterator iter = messages.iterator(); + while (iter.hasNext()) { + msg = (Message) iter.next(); + up_prot.up(new Event(Event.MSG,msg)); + } + } + + // random integer between 0 and n-1 + int rnd(int n) { return (int)(Math.random()*n); } + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/SIZE.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/SIZE.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/SIZE.java 17 Aug 2012 14:51:06 -0000 1.1 @@ -0,0 +1,173 @@ +// $Id: SIZE.java,v 1.1 2012/08/17 14:51:06 marcin Exp $ + +package org.jgroups.protocols; + +import org.jgroups.Event; +import org.jgroups.Message; +import org.jgroups.util.Util; +import org.jgroups.stack.Protocol; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.util.Properties; +import java.util.Vector; + + +/** + * Protocol which prints out the real size of a message. To do this, the message + * is serialized into a byte buffer and its size read. Don't use this layer in + * a production stack since the costs are high (just for debugging). + * + * @author Bela Ban June 13 2001 + */ +public class SIZE extends Protocol { + final Vector members=new Vector(); + boolean print_msg=false; + boolean raw_buffer=false; // just print size of message buffer + + /** Min size in bytes above which msgs should be printed */ + long min_size=0; + + final ByteArrayOutputStream out_stream=new ByteArrayOutputStream(65535); + + + /** + * All protocol names have to be unique ! + */ + public String getName() { + return "SIZE"; + } + + + public void init() { + } + + + /** + * Setup the Protocol instance acording to the configuration string + */ + public boolean setProperties(Properties props) {super.setProperties(props); + String str; + + str=props.getProperty("print_msg"); + if(str != null) { + print_msg=Boolean.valueOf(str).booleanValue(); + props.remove("print_msg"); + } + + str=props.getProperty("raw_buffer"); + if(str != null) { + raw_buffer=Boolean.valueOf(str).booleanValue(); + props.remove("raw_buffer"); + } + + str=props.getProperty("min_size"); + if(str != null) { + min_size=Integer.parseInt(str); + props.remove("min_size"); + } + + if(!props.isEmpty()) { + log.error("the following properties are not recognized: " + props); + + return false; + } + return true; + } + + + public Object up(Event evt) { + Message msg; + int payload_size=0, serialized_size; + + switch(evt.getType()) { + + case Event.MSG: + msg=(Message)evt.getArg(); + payload_size=msg.getLength(); + + if(raw_buffer) { + if(log.isTraceEnabled()) + log.trace("size of message buffer is " + payload_size + ", " + numHeaders(msg) + " headers"); + } + else { + serialized_size=sizeOf(msg); + if(serialized_size > min_size) { + if(log.isTraceEnabled()) + log.trace("size of serialized message is " + serialized_size + + ", " + numHeaders(msg) + " headers"); + + } + } + if(print_msg) { + if(log.isTraceEnabled()) + log.trace("headers are " + msg.printHeaders() + ", payload size=" + payload_size); + } + break; + } + + return up_prot.up(evt); // pass up to the layer above us + } + + + public Object down(Event evt) { + Message msg; + int payload_size=0, serialized_size; + + switch(evt.getType()) { + + case Event.MSG: + msg=(Message)evt.getArg(); + payload_size=msg.getLength(); + + if(raw_buffer) { + if(log.isTraceEnabled()) + log.trace("size of message buffer is " + payload_size + ", " + numHeaders(msg) + " headers"); + } + else { + serialized_size=sizeOf(msg); + if(serialized_size > min_size) { + if(log.isTraceEnabled()) + log.trace("size of serialized message is " + serialized_size + ", " + numHeaders(msg) + " headers"); + + } + } + if(print_msg) { + if(log.isTraceEnabled()) + log.trace("headers are " + msg.printHeaders() + ", payload size=" + payload_size); + } + break; + } + + return down_prot.down(evt); // Pass on to the layer below us + } + + + int sizeOf(Message msg) { + DataOutputStream out=null; + + synchronized(out_stream) { + try { + out_stream.reset(); + out=new DataOutputStream(out_stream); + msg.writeTo(out); + out.flush(); + return out_stream.size(); + } + catch(Exception e) { + return 0; + } + finally { + Util.close(out); + } + } + } + + int numHeaders(Message msg) { + if(msg == null) + return 0; + return msg.getNumHeaders(); + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/SMACK.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/SMACK.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/SMACK.java 17 Aug 2012 14:51:08 -0000 1.1 @@ -0,0 +1,374 @@ + +package org.jgroups.protocols; + + +import org.jgroups.*; +import org.jgroups.stack.AckMcastSenderWindow; +import org.jgroups.stack.AckReceiverWindow; +import org.jgroups.stack.Protocol; +import org.jgroups.stack.StaticInterval; +import org.jgroups.util.Streamable; +import org.jgroups.util.Util; + +import java.io.*; +import java.util.*; + + +/** + * Simple Multicast ACK protocol. A positive acknowledgment-based protocol for reliable delivery of + * multicast messages, which does not need any group membership service. + * Basically works as follows: + *
          + *
        • Sender S sends multicast message M
        • + *
        • When member P receives M, it sends back a unicast ack to S
        • + *
        • When S receives the ack from P, it checks whether P is in its + * membership list. If not, P will be added. This is necessary to retransmit the next message + * sent to P.
        • + *
        • When S sends a multicast message M, all members are added to a + * retransmission entry (containing all members to which the message + * was sent), which is added to a hashmap (keyed by seqno). Whenever + * an ack is received from receiver X, X will be removed from the + * retransmission list for the given seqno. When the retransmission + * list is empty, the seqno will be removed from the hashmap.
        • + *
        • A retransmitter thread in the sender periodically retransmits + * (either via unicast, or multicast) messages for which no ack has + * been received yet
        • + *
        • When a max number of (unsuccessful) retransmissions have been + * exceeded, all remaining members for that seqno are removed from + * the local membership, and the seqno is removed from the hashmap, + * ceasing all retransmissions
        • + *
        + * Advantage of this protocol: no group membership necessary, fast. + * @author Bela Ban Aug 2002 + * @version $Id: SMACK.java,v 1.1 2012/08/17 14:51:08 marcin Exp $ + *
        Fix membershop bug: start a, b, kill b, restart b: b will be suspected by a. + */ +public class SMACK extends Protocol implements AckMcastSenderWindow.RetransmitCommand { + long[] timeout=new long[]{1000,2000,3000}; // retransmit timeouts (for AckMcastSenderWindow) + int max_xmits=10; // max retransmissions (if still no ack, member will be removed) + final Set
        members=new LinkedHashSet
        (); // contains Addresses + AckMcastSenderWindow sender_win=null; + final Map receivers=new HashMap(); // keys=sender (Address), values=AckReceiverWindow + final Map xmit_table=new HashMap(); // keeps track of num xmits / member (keys: mbr, val:num) + Address local_addr=null; // my own address + long seqno=1; // seqno for msgs sent by this sender + long vid=1; // for the fake view changes + boolean print_local_addr=true; + static final String name="SMACK"; + + + + + + public SMACK() { + } + + public String getName() { + return name; + } + + + public boolean setProperties(Properties props) { + String str; + long[] tmp; + + super.setProperties(props); + str=props.getProperty("print_local_addr"); + if(str != null) { + print_local_addr=Boolean.valueOf(str).booleanValue(); + props.remove("print_local_addr"); + } + + str=props.getProperty("timeout"); + if(str != null) { + tmp=Util.parseCommaDelimitedLongs(str); + props.remove("timeout"); + if(tmp != null && tmp.length > 0) + timeout=tmp; + } + + str=props.getProperty("max_xmits"); + if(str != null) { + max_xmits=Integer.parseInt(str); + props.remove("max_xmits"); + } + + if(!props.isEmpty()) { + log.error("the following properties are not recognized: " + props); + return false; + } + return true; + } + + + public void stop() { + AckReceiverWindow win; + if(sender_win != null) { + sender_win.stop(); + sender_win=null; + } + for(Iterator it=receivers.values().iterator(); it.hasNext();) { + win=(AckReceiverWindow)it.next(); + win.reset(); + } + receivers.clear(); + } + + + public Object up(Event evt) { + Address sender; + + switch(evt.getType()) { + + case Event.SET_LOCAL_ADDRESS: + local_addr=(Address)evt.getArg(); + addMember(local_addr); + if(print_local_addr) { + System.out.println("\n-------------------------------------------------------\n" + + "GMS: address is " + local_addr + + "\n-------------------------------------------------------"); + } + break; + + case Event.SUSPECT: + if(log.isInfoEnabled()) log.info("removing suspected member " + evt.getArg()); + removeMember((Address)evt.getArg()); + break; + + case Event.MSG: + Message msg=(Message)evt.getArg(), tmp_msg; + if(msg == null) break; + sender=msg.getSrc(); + SmackHeader hdr=(SmackHeader)msg.getHeader(name); + if(hdr == null) // is probably a unicast message + break; + switch(hdr.type) { + case SmackHeader.MCAST: // send an ack, then pass up (if not already received) + if(log.isTraceEnabled()) + log.trace("received #" + hdr.seqno + " from " + sender); + + AckReceiverWindow win=receivers.get(sender); + if(win == null) { + addMember(sender); + win=new AckReceiverWindow(hdr.seqno); + receivers.put(sender, win); + } + + boolean added=win.add(hdr.seqno, msg); + + Message ack_msg=new Message(sender); + ack_msg.putHeader(name, new SmackHeader(SmackHeader.ACK, hdr.seqno)); + down_prot.down(new Event(Event.MSG, ack_msg)); + + // message is passed up if OOB. Later, when remove() is called, we discard it. This affects ordering ! + // http://jira.jboss.com/jira/browse/JGRP-379 + if(msg.isFlagSet(Message.OOB) && added) { + up_prot.up(new Event(Event.MSG, msg)); + } + + // now remove as many messages as possible + while((tmp_msg=win.remove()) != null) { + // discard OOB msg as it has already been delivered (http://jira.jboss.com/jira/browse/JGRP-379) + if(tmp_msg.isFlagSet(Message.OOB)) { + continue; + } + up_prot.up(new Event(Event.MSG, tmp_msg)); + } + return null; + + case SmackHeader.ACK: + addMember(msg.getSrc()); + sender_win.ack(hdr.seqno, msg.getSrc()); + sender_win.clearStableMessages(); + if(log.isTraceEnabled()) + log.trace("received ack for #" + hdr.seqno + " from " + msg.getSrc()); + return null; + + case SmackHeader.JOIN_ANNOUNCEMENT: + if(log.isInfoEnabled()) log.info("received join announcement by " + msg.getSrc()); + if(!containsMember(sender)) { + Message join_rsp=new Message(sender); + join_rsp.putHeader(name, new SmackHeader(SmackHeader.JOIN_ANNOUNCEMENT, -1)); + down_prot.down(new Event(Event.ENABLE_UNICASTS_TO, sender)); + down_prot.down(new Event(Event.MSG, join_rsp)); + } + addMember(sender); + return null; + + case SmackHeader.LEAVE_ANNOUNCEMENT: + if(log.isInfoEnabled()) log.info("received leave announcement by " + msg.getSrc()); + removeMember(sender); + return null; + + default: + if(log.isWarnEnabled()) log.warn("detected SmackHeader with invalid type: " + hdr); + break; + } + break; + } + + return up_prot.up(evt); + } + + + public Object down(Event evt) { + Message leave_msg; + + switch(evt.getType()) { + + case Event.DISCONNECT: + leave_msg=new Message(); + leave_msg.putHeader(name, new SmackHeader(SmackHeader.LEAVE_ANNOUNCEMENT, -1)); + down_prot.down(new Event(Event.MSG, leave_msg)); + sender_win.stop(); + break; + + case Event.CONNECT: + Object ret=down_prot.down(evt); + sender_win=new AckMcastSenderWindow(this, new StaticInterval(timeout)); + + // send join announcement + Message join_msg=new Message(); + join_msg.putHeader(name, new SmackHeader(SmackHeader.JOIN_ANNOUNCEMENT, -1)); + down_prot.down(new Event(Event.MSG, join_msg)); + return ret; + + + // add a header with the current sequence number and increment seqno + case Event.MSG: + Message msg=(Message)evt.getArg(); + if(msg == null) break; + if(msg.getDest() == null || msg.getDest().isMulticastAddress()) { + msg.putHeader(name, new SmackHeader(SmackHeader.MCAST, seqno)); + sender_win.add(seqno, msg, new Vector
        (members)); + if(log.isTraceEnabled()) log.trace("sending mcast #" + seqno); + seqno++; + } + break; + } + + return down_prot.down(evt); + } + + + + /* ----------------------- Interface AckMcastSenderWindow.RetransmitCommand -------------------- */ + + public void retransmit(long seqno, Message msg, Address dest) { + msg.setDest(dest); + + if(log.isInfoEnabled()) log.info(seqno + ", msg=" + msg); + down_prot.down(new Event(Event.MSG, msg)); + } + + /* -------------------- End of Interface AckMcastSenderWindow.RetransmitCommand ---------------- */ + + + + + public static class SmackHeader extends Header implements Streamable { + public static final byte MCAST=1; + public static final byte ACK=2; + public static final byte JOIN_ANNOUNCEMENT=3; + public static final byte LEAVE_ANNOUNCEMENT=4; + + byte type=0; + long seqno=-1; + private static final long serialVersionUID=7605481696520929774L; + + public SmackHeader() { + } + + public SmackHeader(byte type, long seqno) { + this.type=type; + this.seqno=seqno; + } + + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeByte(type); + out.writeLong(seqno); + } + + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + type=in.readByte(); + seqno=in.readLong(); + } + + public int size() { + return Global.LONG_SIZE + Global.BYTE_SIZE; + } + + public void writeTo(DataOutputStream out) throws IOException { + out.writeByte(type); + out.writeLong(seqno); + } + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + type=in.readByte(); + seqno=in.readLong(); + } + + + public String toString() { + switch(type) { + case MCAST: + return "MCAST"; + case ACK: + return "ACK"; + case JOIN_ANNOUNCEMENT: + return "JOIN_ANNOUNCEMENT"; + case LEAVE_ANNOUNCEMENT: + return "LEAVE_ANNOUNCEMENT"; + default: + return ""; + } + } + } + + + /* ------------------------------------- Private methods --------------------------------------- */ + void addMember(Address mbr) { + Vector
        tmp=null; + synchronized(members) { + if(members.add(mbr)) { + tmp=new Vector
        (members); + } + } + if(tmp != null) { + if(log.isTraceEnabled()) + log.trace("added " + mbr + ", members=" + tmp); + View new_view=new View(new ViewId(local_addr, vid++), tmp); + up_prot.up(new Event(Event.VIEW_CHANGE, new_view)); + down_prot.down(new Event(Event.VIEW_CHANGE, new_view)); + } + } + + void removeMember(Address mbr) { + Vector
        tmp=null; + synchronized(members) { + if(members.remove(mbr)) + tmp=new Vector
        (members); + } + if(tmp != null) { + if(log.isTraceEnabled()) + log.trace("removed " + mbr + ", members=" + tmp); + View new_view=new View(new ViewId(local_addr, vid++), tmp); + up_prot.up(new Event(Event.VIEW_CHANGE, new_view)); + down_prot.down(new Event(Event.VIEW_CHANGE, new_view)); + if(sender_win != null) + sender_win.remove(mbr); // causes retransmissions to mbr to stop + } + } + + + boolean containsMember(Address mbr) { + synchronized(members) { + return members.contains(mbr); + } + } + + /* --------------------------------- End of Private methods ------------------------------------ */ + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/STATS.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/STATS.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/STATS.java 17 Aug 2012 14:51:05 -0000 1.1 @@ -0,0 +1,195 @@ +package org.jgroups.protocols; + +import org.jgroups.stack.Protocol; +import org.jgroups.Event; +import org.jgroups.Message; +import org.jgroups.Address; +import org.jgroups.View; + +import java.util.*; + +/** + * Provides various stats + * @author Bela Ban + * @version $Id: STATS.java,v 1.1 2012/08/17 14:51:05 marcin Exp $ + */ +public class STATS extends Protocol { + long sent_msgs, sent_bytes, sent_ucasts, sent_mcasts, received_ucasts, received_mcasts; + long received_msgs, received_bytes, sent_ucast_bytes, sent_mcast_bytes, received_ucast_bytes, received_mcast_bytes; + + /** HashMap, maintains stats per target destination */ + HashMap sent=new HashMap(); + + /** HashMap, maintains stats per receiver */ + HashMap received=new HashMap(); + + static final short UP=1; + static final short DOWN=2; + + + public String getName() { + return "STATS"; + } + + public boolean setProperties(Properties props) { + super.setProperties(props); + if(!props.isEmpty()) { + log.error("the following properties are not recognized: " + props); + return false; + } + return true; + } + + public void resetStats() { + sent_msgs=sent_bytes=sent_ucasts=sent_mcasts=received_ucasts=received_mcasts=0; + received_msgs=received_bytes=sent_ucast_bytes=sent_mcast_bytes=received_ucast_bytes=received_mcast_bytes=0; + sent.clear(); + received.clear(); + } + + + public long getSentMessages() {return sent_msgs;} + public long getSentBytes() {return sent_bytes;} + public long getSentUnicastMessages() {return sent_ucasts;} + public long getSentUnicastBytes() {return sent_ucast_bytes;} + public long getSentMcastMessages() {return sent_mcasts;} + public long getSentMcastBytes() {return sent_mcast_bytes;} + + public long getReceivedMessages() {return received_msgs;} + public long getReceivedBytes() {return received_bytes;} + public long getReceivedUnicastMessages() {return received_ucasts;} + public long getReceivedUnicastBytes() {return received_ucast_bytes;} + public long getReceivedMcastMessages() {return received_mcasts;} + public long getReceivedMcastBytes() {return received_mcast_bytes;} + + + public Object up(Event evt) { + if(evt.getType() == Event.MSG) { + Message msg=(Message)evt.getArg(); + updateStats(msg, UP); + } + else if(evt.getType() == Event.VIEW_CHANGE) { + handleViewChange((View)evt.getArg()); + } + return up_prot.up(evt); + } + + + + public Object down(Event evt) { + if(evt.getType() == Event.MSG) { + Message msg=(Message)evt.getArg(); + updateStats(msg, DOWN); + } + else if(evt.getType() == Event.VIEW_CHANGE) { + handleViewChange((View)evt.getArg()); + } + return down_prot.down(evt); + } + + + public String printStats() { + Map.Entry entry; + Object key, val; + StringBuilder sb=new StringBuilder(); + sb.append("sent:\n"); + for(Iterator it=sent.entrySet().iterator(); it.hasNext();) { + entry=(Map.Entry)it.next(); + key=entry.getKey(); + if(key == null) key=""; + val=entry.getValue(); + sb.append(key).append(": ").append(val).append("\n"); + } + sb.append("\nreceived:\n"); + for(Iterator it=received.entrySet().iterator(); it.hasNext();) { + entry=(Map.Entry)it.next(); + key=entry.getKey(); + val=entry.getValue(); + sb.append(key).append(": ").append(val).append("\n"); + } + + return sb.toString(); + } + + private void handleViewChange(View view) { + Vector members=view.getMembers(); + Set tmp=new LinkedHashSet(members); + tmp.add(null); // for null destination (= mcast) + sent.keySet().retainAll(tmp); + received.keySet().retainAll(tmp); + } + + private void updateStats(Message msg, short direction) { + int length; + HashMap map; + boolean mcast; + Address dest, src; + + if(msg == null) return; + length=msg.getLength(); + dest=msg.getDest(); + src=msg.getSrc(); + mcast=dest == null || dest.isMulticastAddress(); + + if(direction == UP) { // received + received_msgs++; + received_bytes+=length; + if(mcast) { + received_mcasts++; + received_mcast_bytes+=length; + } + else { + received_ucasts++; + received_ucast_bytes+=length; + } + } + else { // sent + sent_msgs++; + sent_bytes+=length; + if(mcast) { + sent_mcasts++; + sent_mcast_bytes+=length; + } + else { + sent_ucasts++; + sent_ucast_bytes+=length; + } + } + + Address key=direction == UP? src : dest; + map=direction == UP? received : sent; + Entry entry=(Entry)map.get(key); + if(entry == null) { + entry=new Entry(); + map.put(key, entry); + } + entry.msgs++; + entry.bytes+=length; + if(mcast) { + entry.mcasts++; + entry.mcast_bytes+=length; + } + else { + entry.ucasts++; + entry.ucast_bytes+=length; + } + } + + + + + static class Entry { + long msgs, bytes, ucasts, mcasts, ucast_bytes, mcast_bytes; + + public String toString() { + StringBuilder sb=new StringBuilder(); + sb.append(msgs).append(" (").append(bytes).append(" bytes)"); + sb.append(": ").append(ucasts).append(" ucasts (").append(ucast_bytes).append(" bytes), "); + sb.append(mcasts).append(" mcasts (").append(mcast_bytes).append(" bytes)"); + return sb.toString(); + } + } + + + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/TCP.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/TCP.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/TCP.java 17 Aug 2012 14:51:07 -0000 1.1 @@ -0,0 +1,136 @@ +// $Id: TCP.java,v 1.1 2012/08/17 14:51:07 marcin Exp $ + +package org.jgroups.protocols; + + +import org.jgroups.Address; +import org.jgroups.blocks.ConnectionTable; +import org.jgroups.stack.IpAddress; +import org.jgroups.util.PortsManager; + +import java.net.InetAddress; +import java.util.Collection; +import java.util.Properties; + + +/** + * TCP based protocol. Creates a server socket, which gives us the local address of this group member. For + * each accept() on the server socket, a new thread is created that listens on the socket. + * For each outgoing message m, if m.dest is in the ougoing hashtable, the associated socket will be reused + * to send message, otherwise a new socket is created and put in the hashtable. + * When a socket connection breaks or a member is removed from the group, the corresponding items in the + * incoming and outgoing hashtables will be removed as well.
        + * This functionality is in ConnectionTable, which isT used by TCP. TCP sends messages using ct.send() and + * registers with the connection table to receive all incoming messages. + * @author Bela Ban + */ +public class TCP extends BasicTCP implements ConnectionTable.Receiver { // , BasicConnectionTable.ConnectionListener { + private ConnectionTable ct=null; + + + + public TCP() { + } + + public String getName() { + return "TCP"; + } + + + public int getOpenConnections() {return ct.getNumConnections();} + public String printConnections() {return ct.toString();} + + + /** Setup the Protocol instance acording to the configuration string */ + public boolean setProperties(Properties props) { + super.setProperties(props); + if(!props.isEmpty()) { + log.error("the following properties are not recognized: " + props); + return false; + } + return true; + } + + public void send(Address dest, byte[] data, int offset, int length) throws Exception { + ct.send(dest, data, offset, length); + } + + public void retainAll(Collection
        members) { + ct.retainAll(members); + } + + public void start() throws Exception { + ct=getConnectionTable(reaper_interval,conn_expire_time,bind_addr,external_addr,start_port,end_port,pm); + // ct.addConnectionListener(this); + ct.setUseSendQueues(use_send_queues); + ct.setSendQueueSize(send_queue_size); + // ct.addConnectionListener(this); + ct.setReceiveBufferSize(recv_buf_size); + ct.setSendBufferSize(send_buf_size); + ct.setSocketConnectionTimeout(sock_conn_timeout); + ct.setPeerAddressReadTimeout(peer_addr_read_timeout); + ct.setTcpNodelay(tcp_nodelay); + ct.setLinger(linger); + local_addr=ct.getLocalAddress(); + if(additional_data != null && local_addr instanceof IpAddress) + ((IpAddress)local_addr).setAdditionalData(additional_data); + + //http://jira.jboss.com/jira/browse/JGRP-626 + //we first start threads in TP + super.start(); + //and then we kick off acceptor thread for CT + ct.start(); + } + + public void stop() { + //and for stopping we do vice versa + ct.stop(); + super.stop(); + } + + + + + /** + * @param reaperInterval + * @param connExpireTime + * @param bindAddress + * @param startPort + * @throws Exception + * @return ConnectionTable + * Sub classes overrides this method to initialize a different version of + * ConnectionTable. + */ + protected ConnectionTable getConnectionTable(long reaperInterval, long connExpireTime, InetAddress bindAddress, + InetAddress externalAddress, int startPort, int endPort, + PortsManager pm) throws Exception { + ConnectionTable cTable; + if(reaperInterval == 0 && connExpireTime == 0) { + cTable=new ConnectionTable(this, bindAddress, externalAddress, startPort, endPort, pm); + } + else { + if(reaperInterval == 0) { + reaperInterval=5000; + if(log.isWarnEnabled()) log.warn("reaper_interval was 0, set it to " + reaperInterval); + } + if(connExpireTime == 0) { + connExpireTime=1000 * 60 * 5; + if(log.isWarnEnabled()) log.warn("conn_expire_time was 0, set it to " + connExpireTime); + } + cTable=new ConnectionTable(this, bindAddress, externalAddress, startPort, endPort, + reaperInterval, connExpireTime, pm); + } + cTable.setThreadFactory(getThreadFactory()); + return cTable; + } + + +// public void connectionOpened(Address peer_addr) { +// } +// +// public void connectionClosed(Address peer_addr) { +// if(log.isTraceEnabled()) +// log.trace("removing connection to " + peer_addr + " from connection table as peer closed connection"); +// ct.remove(peer_addr); +// } +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/TCPGOSSIP.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/TCPGOSSIP.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/TCPGOSSIP.java 17 Aug 2012 14:51:07 -0000 1.1 @@ -0,0 +1,203 @@ +// $Id: TCPGOSSIP.java,v 1.1 2012/08/17 14:51:07 marcin Exp $ + +package org.jgroups.protocols; + +import org.jgroups.Address; +import org.jgroups.Event; +import org.jgroups.Message; +import org.jgroups.util.Promise; +import org.jgroups.stack.GossipClient; +import org.jgroups.stack.IpAddress; + +import java.util.*; +import java.net.UnknownHostException; + + +/** + * The TCPGOSSIP protocol layer retrieves the initial membership (used by the GMS when started + * by sending event FIND_INITIAL_MBRS down the stack). + * We do this by contacting one or more GossipRouters, which must be running at well-known + * addresses:ports. The responses should allow us to determine the coordinator whom we have to + * contact, e.g. in case we want to join the group. When we are a server (after having + * received the BECOME_SERVER event), we'll respond to TCPGOSSIP requests with a TCPGOSSIP + * response.

        The FIND_INITIAL_MBRS event will eventually be answered with a + * FIND_INITIAL_MBRS_OK event up the stack. + * + * @author Bela Ban + */ +public class TCPGOSSIP extends Discovery { + Vector initial_hosts=null; // (list of IpAddresses) hosts to be contacted for the initial membership + GossipClient gossip_client=null; // accesses the GossipRouter(s) to find initial mbrship + + // we need to refresh the registration with the GossipRouter(s) periodically, + // so that our entries are not purged from the cache + long gossip_refresh_rate=20000; + int sock_conn_timeout=1000; // max time in millis for a socket creation + int sock_read_timeout=3000; // max time in millis for a socket read + final static String name="TCPGOSSIP"; + + + public String getName() { + return name; + } + + + + public boolean setProperties(Properties props) { + String str; + str=props.getProperty("gossip_refresh_rate"); // wait for at most n members + if(str != null) { + gossip_refresh_rate=Integer.parseInt(str); + props.remove("gossip_refresh_rate"); + } + + str=props.getProperty("sock_conn_timeout"); // wait for at most n members + if(str != null) { + sock_conn_timeout=Integer.parseInt(str); + props.remove("sock_conn_timeout"); + } + + str=props.getProperty("sock_read_timeout"); // wait for at most n members + if(str != null) { + sock_read_timeout=Integer.parseInt(str); + props.remove("sock_read_timeout"); + } + + str=props.getProperty("initial_hosts"); + if(str != null) { + props.remove("initial_hosts"); + try { + initial_hosts=createInitialHosts(str); + } + catch(UnknownHostException ex) { + if(log.isErrorEnabled()) + log.error("failed creating initial hosts", ex); + return false; + } + } + + if(initial_hosts == null || initial_hosts.isEmpty()) { + if(log.isErrorEnabled()) log.error("initial_hosts must contain the address of at least one GossipRouter"); + return false; + } + + if(timeout <= sock_conn_timeout) { + log.warn("timeout should be greater than sock_conn_timeout"); + } + + + return super.setProperties(props); + } + + + + public void start() throws Exception { + super.start(); + if(gossip_client == null) { + gossip_client=new GossipClient(initial_hosts, gossip_refresh_rate, sock_conn_timeout); + gossip_client.setSocketReadTimeout(sock_read_timeout); + } + } + + public void stop() { + super.stop(); + if(gossip_client != null) { + gossip_client.stop(); + gossip_client=null; + } + } + + public void destroy() { + if(gossip_client != null) { + gossip_client.destroy(); + gossip_client=null; + } + } + + + public void handleConnect() { + if(group_addr == null || local_addr == null) { + if(log.isErrorEnabled()) + log.error("group_addr or local_addr is null, cannot register with GossipRouter(s)"); + } + else { + if(log.isTraceEnabled()) + log.trace("registering " + local_addr + " under " + group_addr + " with GossipRouter"); + gossip_client.register(group_addr, local_addr); + } + } + + public void handleDisconnect() { + if(group_addr != null && local_addr != null) { + gossip_client.unregister(group_addr, local_addr); + } + } + + public void sendGetMembersRequest(Promise promise) throws Exception{ + Message msg, copy; + PingHeader hdr; + List tmp_mbrs; + Address mbr_addr; + + if(group_addr == null) { + if(log.isErrorEnabled()) log.error("[FIND_INITIAL_MBRS]: group_addr is null, cannot get mbrship"); + return; + } + if(log.isTraceEnabled()) log.trace("fetching members from GossipRouter(s)"); + tmp_mbrs=gossip_client.getMembers(group_addr); + if(tmp_mbrs == null || tmp_mbrs.isEmpty()) { + if(log.isErrorEnabled()) log.error("[FIND_INITIAL_MBRS]: gossip client found no members"); + return; + } + if(log.isTraceEnabled()) log.trace("consolidated mbrs from GossipRouter(s) are " + tmp_mbrs); + + // 1. 'Mcast' GET_MBRS_REQ message + hdr=new PingHeader(PingHeader.GET_MBRS_REQ, null); + msg=new Message(null); + msg.setFlag(Message.OOB); + msg.putHeader(name, hdr); + + for(Iterator it=tmp_mbrs.iterator(); it.hasNext();) { + mbr_addr=(Address)it.next(); + copy=msg.copy(); + copy.setDest(mbr_addr); + if(log.isTraceEnabled()) log.trace("[FIND_INITIAL_MBRS] sending PING request to " + copy.getDest()); + down_prot.down(new Event(Event.MSG, copy)); + } + } + + + + /* -------------------------- Private methods ---------------------------- */ + + + /** + * Input is "daddy[8880],sindhu[8880],camille[5555]. Return list of IpAddresses + */ + private Vector

        createInitialHosts(String l) throws UnknownHostException { + Vector
        tmp=new Vector
        (); + String host; + int port; + IpAddress addr; + StringTokenizer tok=new StringTokenizer(l, ","); + String t; + + while(tok.hasMoreTokens()) { + try { + t=tok.nextToken(); + host=t.substring(0, t.indexOf('[')); + port=Integer.parseInt(t.substring(t.indexOf('[') + 1, t.indexOf(']'))); + addr=new IpAddress(host, port); + tmp.addElement(addr); + } + catch(NumberFormatException e) { + if(log.isErrorEnabled()) log.error("exeption is " + e); + } + } + + return tmp; + } + + +} + Index: 3rdParty_sources/jgroups/org/jgroups/protocols/TCPPING.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/TCPPING.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/TCPPING.java 17 Aug 2012 14:51:06 -0000 1.1 @@ -0,0 +1,160 @@ +// $Id: TCPPING.java,v 1.1 2012/08/17 14:51:06 marcin Exp $ + +package org.jgroups.protocols; + + +import org.jgroups.Address; +import org.jgroups.Event; +import org.jgroups.Message; +import org.jgroups.Global; +import org.jgroups.util.Util; +import org.jgroups.util.Promise; +import org.jgroups.stack.IpAddress; + +import java.util.*; +import java.net.UnknownHostException; + + +/** + * The TCPPING protocol layer retrieves the initial membership in answer to the GMS's + * FIND_INITIAL_MBRS event. The initial membership is retrieved by directly contacting other group + * members, sending point-to-point mebership requests. The responses should allow us to determine + * the coordinator whom we have to contact in case we want to join the group. When we are a server + * (after having received the BECOME_SERVER event), we'll respond to TCPPING requests with a TCPPING + * response. + *

        + * The FIND_INITIAL_MBRS event will eventually be answered with a FIND_INITIAL_MBRS_OK event up + * the stack. + *

        + * The TCPPING protocol requires a static conifiguration, which assumes that you to know in advance + * where to find other members of your group. For dynamic discovery, use the PING protocol, which + * uses multicast discovery, or the TCPGOSSIP protocol, which contacts a Gossip Router to acquire + * the initial membership. + * + * @author Bela Ban + */ +public class TCPPING extends Discovery { + int port_range=1; // number of ports to be probed for initial membership + + /** List */ + List

        initial_hosts=null; // hosts to be contacted for the initial membership + final static String name="TCPPING"; + + + + public String getName() { + return name; + } + + /** + * Returns the list of initial hosts as configured by the user via XML. Note that the returned list is mutable, so + * careful with changes ! + * @return List
        list of initial hosts. This variable is only set after the channel has been created and + * set Properties() has been called + */ + public List
        getInitialHosts() { + return initial_hosts; + } + + + public boolean setProperties(Properties props) { + String str; + this.props.putAll(props); // redundant + + str=props.getProperty("port_range"); // if member cannot be contacted on base port, + if(str != null) { // how many times can we increment the port + port_range=Integer.parseInt(str); + if (port_range < 1) { + port_range = 1; + } + props.remove("port_range"); + } + + str=Util.getProperty(new String[]{Global.TCPPING_INITIAL_HOSTS}, props, "initial_hosts", false, null); + if(str != null) { + props.remove("initial_hosts"); + try { + initial_hosts=createInitialHosts(str); + } + catch(UnknownHostException e) { + log.error("failed creating initial list of hosts", e); + return false; + } + } + + return super.setProperties(props); + } + + + public void localAddressSet(Address addr) { + // Add own address to initial_hosts if not present: we must always be able to ping ourself ! + if(initial_hosts != null && addr != null) { + if(initial_hosts.contains(addr)) { + initial_hosts.remove(addr); + if(log.isDebugEnabled()) log.debug("[SET_LOCAL_ADDRESS]: removing my own address (" + addr + + ") from initial_hosts; initial_hosts=" + initial_hosts); + } + } + } + + + public void sendGetMembersRequest(Promise promise) throws Exception { + + for(Iterator
        it = initial_hosts.iterator();it.hasNext();){ + final Address addr = it.next(); + final Message msg = new Message(addr, null, null); + msg.setFlag(Message.OOB); + msg.putHeader(name, new PingHeader(PingHeader.GET_MBRS_REQ, null)); + + if(log.isTraceEnabled()) + log.trace("[FIND_INITIAL_MBRS] sending PING request to " + msg.getDest()); + + down_prot.down(new Event(Event.MSG, msg)); + + timer.submit(new Runnable() { + public void run() { + try{ + down_prot.down(new Event(Event.MSG, msg)); + }catch(Exception ex){ + if(log.isErrorEnabled()) + log.error("failed sending discovery request to " + addr, ex); + } + } + }); + } + } + + + + /* -------------------------- Private methods ---------------------------- */ + + /** + * Input is "daddy[8880],sindhu[8880],camille[5555]. Return List of IpAddresses + */ + private List
        createInitialHosts(String l) throws UnknownHostException { + StringTokenizer tok=new StringTokenizer(l, ","); + String t; + IpAddress addr; + List
        retval=new ArrayList
        (); + + while(tok.hasMoreTokens()) { + try { + t=tok.nextToken().trim(); + String host=t.substring(0, t.indexOf('[')); + host=host.trim(); + int port=Integer.parseInt(t.substring(t.indexOf('[') + 1, t.indexOf(']'))); + for(int i=port; i < port + port_range; i++) { + addr=new IpAddress(host, i); + retval.add(addr); + } + } + catch(NumberFormatException e) { + if(log.isErrorEnabled()) log.error("exeption is " + e); + } + } + + return retval; + } + +} + Index: 3rdParty_sources/jgroups/org/jgroups/protocols/TCP_NIO.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/TCP_NIO.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/TCP_NIO.java 17 Aug 2012 14:51:08 -0000 1.1 @@ -0,0 +1,164 @@ +package org.jgroups.protocols; + +import org.jgroups.blocks.ConnectionTableNIO; +import org.jgroups.blocks.BasicConnectionTable; +import org.jgroups.Address; +import org.jgroups.stack.IpAddress; +import org.jgroups.util.PortsManager; + +import java.net.InetAddress; +import java.util.Properties; +import java.util.Collection; + +/** + * Transport using NIO + * @author Scott Marlow + * @author Alex Fu + * @author Bela Ban + * @version $Id: TCP_NIO.java,v 1.1 2012/08/17 14:51:08 marcin Exp $ + */ +public class TCP_NIO extends BasicTCP implements BasicConnectionTable.Receiver +{ + + /* + * (non-Javadoc) + * + * @see org.jgroups.protocols.TCP#getConnectionTable(long, long) + */ + protected ConnectionTableNIO getConnectionTable(long ri, long cet, + InetAddress b_addr, InetAddress bc_addr, + int s_port, int e_port, PortsManager pm) throws Exception { + ConnectionTableNIO retval=null; + if (ri == 0 && cet == 0) { + retval = new ConnectionTableNIO(this, b_addr, bc_addr, s_port, e_port, pm, false ); + } + else { + if (ri == 0) { + ri = 5000; + if(log.isWarnEnabled()) log.warn("reaper_interval was 0, set it to " + ri); + } + if (cet == 0) { + cet = 1000 * 60 * 5; + if(log.isWarnEnabled()) log.warn("conn_expire_time was 0, set it to " + cet); + } + retval = new ConnectionTableNIO(this, b_addr, bc_addr, s_port, e_port, pm, ri, cet, false); + } + retval.setThreadFactory(getThreadFactory()); + retval.setProcessorMaxThreads(getProcessorMaxThreads()); + retval.setProcessorQueueSize(getProcessorQueueSize()); + retval.setProcessorMinThreads(getProcessorMinThreads()); + retval.setProcessorKeepAliveTime(getProcessorKeepAliveTime()); + retval.setProcessorThreads(getProcessorThreads()); + retval.start(); + return retval; + } + + public String printConnections() {return ct.toString();} + + public void send(Address dest, byte[] data, int offset, int length) throws Exception { + ct.send(dest, data, offset, length); + } + + public void start() throws Exception { + ct=getConnectionTable(reaper_interval,conn_expire_time,bind_addr,external_addr,start_port,end_port,pm); + ct.setUseSendQueues(use_send_queues); + // ct.addConnectionListener(this); + ct.setReceiveBufferSize(recv_buf_size); + ct.setSendBufferSize(send_buf_size); + ct.setSocketConnectionTimeout(sock_conn_timeout); + ct.setPeerAddressReadTimeout(peer_addr_read_timeout); + ct.setTcpNodelay(tcp_nodelay); + ct.setLinger(linger); + local_addr=ct.getLocalAddress(); + if(additional_data != null && local_addr instanceof IpAddress) + ((IpAddress)local_addr).setAdditionalData(additional_data); + super.start(); + } + + public void retainAll(Collection
        members) { + ct.retainAll(members); + } + + public void stop() { + ct.stop(); + super.stop(); + } + + public String getName() { + return "TCP_NIO"; + } + + public int getReaderThreads() { return m_reader_threads; } + public int getWriterThreads() { return m_writer_threads; } + public int getProcessorThreads() { return m_processor_threads; } + public int getProcessorMinThreads() { return m_processor_minThreads;} + public int getProcessorMaxThreads() { return m_processor_maxThreads;} + public int getProcessorQueueSize() { return m_processor_queueSize; } + public long getProcessorKeepAliveTime() { return m_processor_keepAliveTime; } + public int getOpenConnections() {return ct.getNumConnections();} + + + + /** Setup the Protocol instance acording to the configuration string */ + public boolean setProperties(Properties props) { + String str; + + str=props.getProperty("reader_threads"); + if(str != null) { + m_reader_threads=Integer.parseInt(str); + props.remove("reader_threads"); + } + + str=props.getProperty("writer_threads"); + if(str != null) { + m_writer_threads=Integer.parseInt(str); + props.remove("writer_threads"); + } + + str=props.getProperty("processor_threads"); + if(str != null) { + m_processor_threads=Integer.parseInt(str); + props.remove("processor_threads"); + } + + str=props.getProperty("processor_minThreads"); + if(str != null) { + m_processor_minThreads=Integer.parseInt(str); + props.remove("processor_minThreads"); + } + + str=props.getProperty("processor_maxThreads"); + if(str != null) { + m_processor_maxThreads =Integer.parseInt(str); + props.remove("processor_maxThreads"); + } + + str=props.getProperty("processor_queueSize"); + if(str != null) { + m_processor_queueSize=Integer.parseInt(str); + props.remove("processor_queueSize"); + } + + str=props.getProperty("processor_keepAliveTime"); + if(str != null) { + m_processor_keepAliveTime=Long.parseLong(str); + props.remove("processor_keepAliveTime"); + } + + return super.setProperties(props); + } + + private int m_reader_threads = 3; + + private int m_writer_threads = 3; + + private int m_processor_threads = 5; // PooledExecutor.createThreads() + private int m_processor_minThreads = 5; // PooledExecutor.setMinimumPoolSize() + private int m_processor_maxThreads = 5; // PooledExecutor.setMaxThreads() + private int m_processor_queueSize=100; // Number of queued requests that can be pending waiting + // for a background thread to run the request. + private long m_processor_keepAliveTime = Long.MAX_VALUE; // PooledExecutor.setKeepAliveTime( milliseconds); + // negative value used to mean (before 2.5 release) to wait forever, + // instead set to Long.MAX_VALUE to keep alive forever + private ConnectionTableNIO ct; +} \ No newline at end of file Index: 3rdParty_sources/jgroups/org/jgroups/protocols/TP.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/TP.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/TP.java 17 Aug 2012 14:51:05 -0000 1.1 @@ -0,0 +1,2338 @@ +package org.jgroups.protocols; + + +import org.jgroups.*; +import org.jgroups.annotations.GuardedBy; +import org.jgroups.stack.IpAddress; +import org.jgroups.stack.Protocol; +import org.jgroups.stack.ProtocolStack; +import org.jgroups.util.*; +import org.jgroups.util.Queue; +import org.jgroups.util.ThreadFactory; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.DataOutputStream; +import java.net.*; +import java.text.NumberFormat; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.locks.ReentrantLock; + + +/** + * Generic transport - specific implementations should extend this abstract class. + * Features which are provided to the subclasses include + *
          + *
        • version checking + *
        • marshalling and unmarshalling + *
        • message bundling (handling single messages, and message lists) + *
        • incoming packet handler + *
        • loopback + *
        + * A subclass has to override + *
          + *
        • {@link #sendToAllMembers(byte[], int, int)} + *
        • {@link #sendToSingleMember(org.jgroups.Address, byte[], int, int)} + *
        • {@link #init()} + *
        • {@link #start()}: subclasses must call super.start() after they initialize themselves + * (e.g., created their sockets). + *
        • {@link #stop()}: subclasses must call super.stop() after they deinitialized themselves + *
        • {@link #destroy()} + *
        + * The create() or start() method has to create a local address.
        of interfaces to receive multicasts on. The multicast receive socket will listen + * on all of these interfaces. This is a comma-separated list of IP addresses or interface names. E.g. + * "192.168.5.1,eth1,127.0.0.1". Duplicates are discarded; we only bind to an interface once. + * If this property is set, it override receive_on_all_interfaces. + */ + List receive_interfaces=null; + + /** If true, the transport should use all available interfaces to send multicast messages. This means + * the same multicast message is sent N times, so use with care */ + boolean send_on_all_interfaces=false; + + /** List of interfaces to send multicasts on. The multicast send socket will send the + * same multicast message on all of these interfaces. This is a comma-separated list of IP addresses or + * interface names. E.g. "192.168.5.1,eth1,127.0.0.1". Duplicates are discarded. + * If this property is set, it override send_on_all_interfaces. + */ + List send_interfaces=null; + + + /** The port to which the transport binds. 0 means to bind to any (ephemeral) port */ + int bind_port=0; + int port_range=1; // 27-6-2003 bgooren, Only try one port by default + + /** The members of this group (updated when a member joins or leaves) */ + final protected HashSet
        members=new HashSet
        (11); + + protected View view=null; + + final ExposedByteArrayInputStream in_stream=new ExposedByteArrayInputStream(new byte[]{'0'}); + final DataInputStream dis=new DataInputStream(in_stream); + + + /** If true, messages sent to self are treated specially: unicast messages are + * looped back immediately, multicast messages get a local copy first and - + * when the real copy arrives - it will be discarded. Useful for Window + * media (non)sense */ + boolean loopback=false; + + + /** Discard packets with a different version. Usually minor version differences are okay. Setting this property + * to true means that we expect the exact same version on all incoming packets */ + protected boolean discard_incompatible_packets=false; + + /** Sometimes receivers are overloaded (they have to handle de-serialization etc). + * Packet handler is a separate thread taking care of de-serialization, receiver + * thread(s) simply put packet in queue and return immediately. Setting this to + * true adds one more thread */ + boolean use_incoming_packet_handler=true; + + /** Used by packet handler to store incoming DatagramPackets */ + Queue incoming_packet_queue=null; + + /** Dequeues DatagramPackets from packet_queue, unmarshalls them and + * calls handleIncomingUdpPacket() */ + IncomingPacketHandler incoming_packet_handler=null; + + + /** Used by packet handler to store incoming Messages */ + Queue incoming_msg_queue=null; + + IncomingMessageHandler incoming_msg_handler; + + + boolean use_concurrent_stack=true; + ThreadGroup pool_thread_group=new ThreadGroup(Util.getGlobalThreadGroup(), "Thread Pools"); + + /** + * Names the current thread. Valid values are "pcl": + * p: include the previous (original) name, e.g. "Incoming thread-1", "UDP ucast receiver" + * c: include the cluster name, e.g. "MyCluster" + * l: include the local address of the current member, e.g. "192.168.5.1:5678" + */ + protected String thread_naming_pattern="cl"; + + public String getThreadNamingPattern() {return thread_naming_pattern;} + + + /** Keeps track of connects and disconnects, in order to start and stop threads */ + int connect_count=0; + + /** Number of times init() was called. Incremented on init(), decremented on destroy() */ + int init_count=0; + + private final ReentrantLock connectLock = new ReentrantLock(); + + + /** ================================== OOB thread pool ============================== */ + /** The thread pool which handles OOB messages */ + Executor oob_thread_pool; + /** Factory which is used by oob_thread_pool */ + ThreadFactory oob_thread_factory=null; + boolean oob_thread_pool_enabled=true; + int oob_thread_pool_min_threads=2; + int oob_thread_pool_max_threads=10; + /** Number of milliseconds after which an idle thread is removed */ + long oob_thread_pool_keep_alive_time=30000; + + long num_oob_msgs_received=0; + + /** Used if oob_thread_pool is a ThreadPoolExecutor and oob_thread_pool_queue_enabled is true */ + BlockingQueue oob_thread_pool_queue=null; + /** Whether of not to use a queue with ThreadPoolExecutor (ignored with direct executor) */ + boolean oob_thread_pool_queue_enabled=true; + /** max number of elements in queue (bounded) */ + int oob_thread_pool_queue_max_size=500; + /** Possible values are "Abort", "Discard", "DiscardOldest" and "Run". These values might change once we switch to + * JDK 5's java.util.concurrent package */ + String oob_thread_pool_rejection_policy="Run"; + + public Executor getOOBThreadPool() { + return oob_thread_pool; + } + + public void setOOBThreadPool(Executor oob_thread_pool) { + if(this.oob_thread_pool != null) { + shutdownThreadPool(this.oob_thread_pool); + } + this.oob_thread_pool=oob_thread_pool; + } + + public ThreadFactory getOOBThreadPoolThreadFactory() { + return oob_thread_factory; + } + + public void setOOBThreadPoolThreadFactory(ThreadFactory factory) { + oob_thread_factory=factory; + if(oob_thread_pool instanceof ThreadPoolExecutor) + ((ThreadPoolExecutor)oob_thread_pool).setThreadFactory(factory); + } + + /** ================================== Regular thread pool ============================== */ + + /** The thread pool which handles unmarshalling, version checks and dispatching of regular messages */ + Executor thread_pool; + /** Factory which is used by oob_thread_pool */ + ThreadFactory default_thread_factory=null; + + boolean thread_pool_enabled=true; + int thread_pool_min_threads=2; + int thread_pool_max_threads=10; + /** Number of milliseconds after which an idle thread is removed */ + long thread_pool_keep_alive_time=30000; + + long num_incoming_msgs_received=0; + + /** Used if thread_pool is a ThreadPoolExecutor and thread_pool_queue_enabled is true */ + BlockingQueue thread_pool_queue=null; + /** Whether of not to use a queue with ThreadPoolExecutor (ignored with directE decutor) */ + boolean thread_pool_queue_enabled=true; + /** max number of elements in queue (bounded) */ + int thread_pool_queue_max_size=500; + /** Possible values are "Abort", "Discard", "DiscardOldest" and "Run". These values might change once we switch to + * JDK 5's java.util.concurrent package */ + String thread_pool_rejection_policy="Run"; + + public Executor getDefaultThreadPool() { + return thread_pool; + } + + public void setDefaultThreadPool(Executor thread_pool) { + if(this.thread_pool != null) + shutdownThreadPool(this.thread_pool); + this.thread_pool=thread_pool; + } + + public ThreadFactory getDefaultThreadPoolThreadFactory() { + return default_thread_factory; + } + + public void setDefaultThreadPoolThreadFactory(ThreadFactory factory) { + default_thread_factory=factory; + if(thread_pool instanceof ThreadPoolExecutor) + ((ThreadPoolExecutor)thread_pool).setThreadFactory(factory); + } + + /** ================================== Timer thread pool ================================= */ + protected TimeScheduler timer=null; + + protected ThreadFactory timer_thread_factory; + + /** Max number of threads to be used by the timer thread pool */ + int num_timer_threads=4; + + public ThreadFactory getTimerThreadFactory() { + return timer_thread_factory; + } + + public void setTimerThreadFactory(ThreadFactory factory) { + timer_thread_factory=factory; + timer.setThreadFactory(factory); + } + + public TimeScheduler getTimer() {return timer;} + + /** =================================Default thread factory ================================== */ + /** Used by all threads created by JGroups outside of the thread pools */ + protected ThreadFactory global_thread_factory=null; + + public ThreadFactory getThreadFactory() { + return global_thread_factory; + } + + public void setThreadFactory(ThreadFactory factory) { + global_thread_factory=factory; + } + + /** ============================= End of default thread factory ============================== */ + + + + /** If set it will be added to local_addr. Used to implement + * for example transport independent addresses */ + byte[] additional_data=null; + + /** Maximum number of bytes for messages to be queued until they are sent. This value needs to be smaller + than the largest datagram packet size in case of UDP */ + int max_bundle_size=64000; + + /** Max number of milliseconds until queued messages are sent. Messages are sent when max_bundle_size or + * max_bundle_timeout has been exceeded (whichever occurs faster) + */ + long max_bundle_timeout=20; + + /** Enable bundling of smaller messages into bigger ones */ + boolean enable_bundling=false; + + /** Enable bundling for unicast messages. Ignored if enable_bundling is off */ + boolean enable_unicast_bundling=true; + + private Bundler bundler=null; + + + private DiagnosticsHandler diag_handler=null; + private final List preregistered_probe_handlers=new LinkedList(); + boolean enable_diagnostics=true; + String diagnostics_addr="224.0.75.75"; + int diagnostics_port=7500; + + /** If this transport is shared, identifies all the transport instances which are to be shared */ + String singleton_name=null; + + /** If singleton_name is enabled, this map is used to de-multiplex incoming messages according to their + * cluster names (attached to the message by the transport anyway). The values are the next protocols above + * the transports. + */ + private final ConcurrentMap up_prots=new ConcurrentHashMap(); + + TpHeader header; + final String name=getName(); + + protected PortsManager pm=null; + protected String persistent_ports_file=null; + protected long pm_expiry_time=30000L; + protected boolean persistent_ports=false; + + static final byte LIST = 1; // we have a list of messages rather than a single message when set + static final byte MULTICAST = 2; // message is a multicast (versus a unicast) message when set + static final byte OOB = 4; // message has OOB flag set (Message.OOB) + + long num_msgs_sent=0, num_msgs_received=0, num_bytes_sent=0, num_bytes_received=0; + + static NumberFormat f; + private static final int INITIAL_BUFSIZE=4095; + + static { + f=NumberFormat.getNumberInstance(); + f.setGroupingUsed(false); + f.setMaximumFractionDigits(2); + } + + + + + /** + * Creates the TP protocol, and initializes the + * state variables, does however not start any sockets or threads. + */ + protected TP() { + } + + /** + * debug only + */ + public String toString() { + return name + "(local address: " + local_addr + ')'; + } + + public void resetStats() { + num_msgs_sent=num_msgs_received=num_bytes_sent=num_bytes_received=0; + num_oob_msgs_received=num_incoming_msgs_received=0; + } + + public void registerProbeHandler(ProbeHandler handler) { + if(diag_handler != null) + diag_handler.registerProbeHandler(handler); + else + preregistered_probe_handlers.add(handler); + } + + public void unregisterProbeHandler(ProbeHandler handler) { + if(diag_handler != null) + diag_handler.unregisterProbeHandler(handler); + } + + public long getNumMessagesSent() {return num_msgs_sent;} + public long getNumMessagesReceived() {return num_msgs_received;} + public long getNumBytesSent() {return num_bytes_sent;} + public long getNumBytesReceived() {return num_bytes_received;} + public String getBindAddress() {return bind_addr != null? bind_addr.toString() : "null";} + public void setBindAddress(String bind_addr) throws UnknownHostException { + this.bind_addr=InetAddress.getByName(bind_addr); + } + public int getBindPort() {return bind_port;} + public void setBindPort(int port) {this.bind_port=port;} + /** @deprecated Use {@link #isReceiveOnAllInterfaces()} instead */ + public boolean getBindToAllInterfaces() {return receive_on_all_interfaces;} + public void setBindToAllInterfaces(boolean flag) {this.receive_on_all_interfaces=flag;} + + public boolean isReceiveOnAllInterfaces() {return receive_on_all_interfaces;} + public java.util.List getReceiveInterfaces() {return receive_interfaces;} + public boolean isSendOnAllInterfaces() {return send_on_all_interfaces;} + public java.util.List getSendInterfaces() {return send_interfaces;} + public boolean isDiscardIncompatiblePackets() {return discard_incompatible_packets;} + public void setDiscardIncompatiblePackets(boolean flag) {discard_incompatible_packets=flag;} + public boolean isEnableBundling() {return enable_bundling;} + public void setEnableBundling(boolean flag) {enable_bundling=flag;} + + public boolean isEnable_unicast_bundling() { + return enable_unicast_bundling; + } + + public void setEnable_unicast_bundling(boolean enable_unicast_bundling) { + this.enable_unicast_bundling=enable_unicast_bundling; + } + + public int getMaxBundleSize() {return max_bundle_size;} + public void setMaxBundleSize(int size) {max_bundle_size=size;} + public long getMaxBundleTimeout() {return max_bundle_timeout;} + public void setMaxBundleTimeout(long timeout) {max_bundle_timeout=timeout;} + public Address getLocalAddress() {return local_addr;} + public String getChannelName() {return channel_name;} + public boolean isLoopback() {return loopback;} + public void setLoopback(boolean b) {loopback=b;} + public boolean isUseIncomingPacketHandler() {return use_incoming_packet_handler;} + public boolean isDefaulThreadPoolEnabled() { return thread_pool_enabled; } + public boolean isOOBThreadPoolEnabled() { return oob_thread_pool_enabled; } + + public ConcurrentMap getUpProtocols() { + return up_prots; + } + + public int getOOBMinPoolSize() { + return oob_thread_pool instanceof ThreadPoolExecutor? ((ThreadPoolExecutor)oob_thread_pool).getCorePoolSize() : 0; + } + + public void setOOBMinPoolSize(int size) { + if(oob_thread_pool instanceof ThreadPoolExecutor) + ((ThreadPoolExecutor)oob_thread_pool).setCorePoolSize(size); + } + + public int getOOBMaxPoolSize() { + return oob_thread_pool instanceof ThreadPoolExecutor? ((ThreadPoolExecutor)oob_thread_pool).getMaximumPoolSize() : 0; + } + + public void setOOBMaxPoolSize(int size) { + if(oob_thread_pool instanceof ThreadPoolExecutor) + ((ThreadPoolExecutor)oob_thread_pool).setMaximumPoolSize(size); + } + + public int getOOBPoolSize() { + return oob_thread_pool instanceof ThreadPoolExecutor? ((ThreadPoolExecutor)oob_thread_pool).getPoolSize() : 0; + } + + public long getOOBKeepAliveTime() { + return oob_thread_pool instanceof ThreadPoolExecutor? ((ThreadPoolExecutor)oob_thread_pool).getKeepAliveTime(TimeUnit.MILLISECONDS) : 0; + } + + public void setOOBKeepAliveTime(long time) { + if(oob_thread_pool instanceof ThreadPoolExecutor) + ((ThreadPoolExecutor)oob_thread_pool).setKeepAliveTime(time, TimeUnit.MILLISECONDS); + } + + public long getOOBMessages() { + return num_oob_msgs_received; + } + + public int getOOBQueueSize() { + return oob_thread_pool_queue.size(); + } + + public int getOOBMaxQueueSize() { + return oob_thread_pool_queue_max_size; + } + + + + + public int getIncomingMinPoolSize() { + return thread_pool instanceof ThreadPoolExecutor? ((ThreadPoolExecutor)thread_pool).getCorePoolSize() : 0; + } + + public void setIncomingMinPoolSize(int size) { + if(thread_pool instanceof ThreadPoolExecutor) + ((ThreadPoolExecutor)thread_pool).setCorePoolSize(size); + } + + public int getIncomingMaxPoolSize() { + return thread_pool instanceof ThreadPoolExecutor? ((ThreadPoolExecutor)thread_pool).getMaximumPoolSize() : 0; + } + + public void setIncomingMaxPoolSize(int size) { + if(thread_pool instanceof ThreadPoolExecutor) + ((ThreadPoolExecutor)thread_pool).setMaximumPoolSize(size); + } + + public int getIncomingPoolSize() { + return thread_pool instanceof ThreadPoolExecutor? ((ThreadPoolExecutor)thread_pool).getPoolSize() : 0; + } + + public long getIncomingKeepAliveTime() { + return thread_pool instanceof ThreadPoolExecutor? ((ThreadPoolExecutor)thread_pool).getKeepAliveTime(TimeUnit.MILLISECONDS) : 0; + } + + public void setIncomingKeepAliveTime(long time) { + if(thread_pool instanceof ThreadPoolExecutor) + ((ThreadPoolExecutor)thread_pool).setKeepAliveTime(time, TimeUnit.MILLISECONDS); + } + + public long getIncomingMessages() { + return num_incoming_msgs_received; + } + + public int getIncomingQueueSize() { + return thread_pool_queue.size(); + } + + public int getIncomingMaxQueueSize() { + return thread_pool_queue_max_size; + } + + + + + + + public Map dumpStats() { + Map retval=super.dumpStats(); + if(retval == null) + retval=new HashMap(); + retval.put("num_msgs_sent", new Long(num_msgs_sent)); + retval.put("num_msgs_received", new Long(num_msgs_received)); + retval.put("num_bytes_sent", new Long(num_bytes_sent)); + retval.put("num_bytes_received", new Long(num_bytes_received)); + return retval; + } + + + /** + * Send to all members in the group. UDP would use an IP multicast message, whereas TCP would send N + * messages, one for each member + * @param data The data to be sent. This is not a copy, so don't modify it + * @param offset + * @param length + * @throws Exception + */ + public abstract void sendToAllMembers(byte[] data, int offset, int length) throws Exception; + + /** + * Send to all members in the group. UDP would use an IP multicast message, whereas TCP would send N + * messages, one for each member + * @param dest Must be a non-null unicast address + * @param data The data to be sent. This is not a copy, so don't modify it + * @param offset + * @param length + * @throws Exception + */ + public abstract void sendToSingleMember(Address dest, byte[] data, int offset, int length) throws Exception; + + public abstract String getInfo(); + + public abstract void postUnmarshalling(Message msg, Address dest, Address src, boolean multicast); + + public abstract void postUnmarshallingList(Message msg, Address dest, boolean multicast); + + + private StringBuilder _getInfo(Channel ch) { + StringBuilder sb=new StringBuilder(); + sb.append(ch.getLocalAddress()).append(" (").append(ch.getClusterName()).append(") ").append("\n"); + sb.append("local_addr=").append(ch.getLocalAddress()).append("\n"); + sb.append("group_name=").append(ch.getClusterName()).append("\n"); + sb.append("version=").append(Version.description).append(", cvs=\"").append(Version.cvs).append("\"\n"); + sb.append("view: ").append(ch.getView()).append('\n'); + sb.append(getInfo()); + return sb; + } + + + private void handleDiagnosticProbe(SocketAddress sender, DatagramSocket sock, String request) { + if(singleton_name != null && singleton_name.length() > 0) { + for(Protocol prot: up_prots.values()) { + ProtocolStack st=prot.getProtocolStack(); + handleDiagnosticProbe(sender, sock, request, st); + } + } + else { + handleDiagnosticProbe(sender, sock, request, stack); + } + } + + + private void handleDiagnosticProbe(SocketAddress sender, DatagramSocket sock, String request, ProtocolStack stack) { + try { + StringTokenizer tok=new StringTokenizer(request); + String req=tok.nextToken(); + StringBuilder info=new StringBuilder("n/a"); + if(req.trim().toLowerCase().startsWith("query")) { + ArrayList l=new ArrayList(tok.countTokens()); + while(tok.hasMoreTokens()) + l.add(tok.nextToken().trim().toLowerCase()); + + info=_getInfo(stack.getChannel()); + + if(l.contains("jmx")) { + Channel ch=stack.getChannel(); + if(ch != null) { + Map m=ch.dumpStats(); + StringBuilder sb=new StringBuilder(); + sb.append("stats:\n"); + for(Iterator> it=m.entrySet().iterator(); it.hasNext();) { + sb.append(it.next()).append("\n"); + } + info.append(sb); + } + } + if(l.contains("props")) { + String p=stack.printProtocolSpecAsXML(); + info.append("\nprops:\n").append(p); + } + if(l.contains("info")) { + Map tmp=stack.getChannel().getInfo(); + info.append("INFO:\n"); + for(Map.Entry entry: tmp.entrySet()) { + info.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); + } + } + } + + byte[] diag_rsp=info.toString().getBytes(); + if(log.isDebugEnabled()) + log.debug("sending diag response to " + sender); + sendResponse(sock, sender, diag_rsp); + } + catch(Throwable t) { + if(log.isErrorEnabled()) + log.error("failed sending diag rsp to " + sender, t); + } + } + + private static void sendResponse(DatagramSocket sock, SocketAddress sender, byte[] buf) throws IOException { + DatagramPacket p=new DatagramPacket(buf, 0, buf.length, sender); + sock.send(p); + } + + /* ------------------------------------------------------------------------------- */ + + + + /*------------------------------ Protocol interface ------------------------------ */ + + + public void init() throws Exception { + if(init_count++ >= 1) { + return; + } + + super.init(); + + // Create the default thread factory + global_thread_factory=new DefaultThreadFactory(Util.getGlobalThreadGroup(), "", false); + + // Create the timer and the associated thread factory - depends on singleton_name + // timer_thread_factory=new DefaultThreadFactory(Util.getGlobalThreadGroup(), "Timer", true, true); + timer_thread_factory=new LazyThreadFactory(Util.getGlobalThreadGroup(), "Timer", true, true); + if(singleton_name != null && singleton_name.trim().length() > 0) { + timer_thread_factory.setIncludeClusterName(false); + } + + default_thread_factory=new DefaultThreadFactory(pool_thread_group, "Incoming", false, true); + + oob_thread_factory=new DefaultThreadFactory(pool_thread_group, "OOB", false, true); + + setInAllThreadFactories(channel_name, local_addr, thread_naming_pattern); + + timer=new TimeScheduler(timer_thread_factory, num_timer_threads); + + verifyRejectionPolicy(oob_thread_pool_rejection_policy); + verifyRejectionPolicy(thread_pool_rejection_policy); + + // ========================================== OOB thread pool ============================== + + if(oob_thread_pool_enabled) { + if(oob_thread_pool_queue_enabled) + oob_thread_pool_queue=new LinkedBlockingQueue(oob_thread_pool_queue_max_size); + else + oob_thread_pool_queue=new SynchronousQueue(); + oob_thread_pool=createThreadPool(oob_thread_pool_min_threads, oob_thread_pool_max_threads, oob_thread_pool_keep_alive_time, + oob_thread_pool_rejection_policy, oob_thread_pool_queue, oob_thread_factory); + } + else { // otherwise use the caller's thread to unmarshal the byte buffer into a message + oob_thread_pool=new DirectExecutor(); + } + + // ====================================== Regular thread pool =========================== + + if(thread_pool_enabled) { + if(thread_pool_queue_enabled) + thread_pool_queue=new LinkedBlockingQueue(thread_pool_queue_max_size); + else + thread_pool_queue=new SynchronousQueue(); + thread_pool=createThreadPool(thread_pool_min_threads, thread_pool_max_threads, thread_pool_keep_alive_time, + thread_pool_rejection_policy, thread_pool_queue, default_thread_factory); + } + else { // otherwise use the caller's thread to unmarshal the byte buffer into a message + thread_pool=new DirectExecutor(); + } + + if(persistent_ports){ + pm = new PortsManager(pm_expiry_time,persistent_ports_file); + } + if(bind_addr != null) { + Map m=new HashMap(1); + m.put("bind_addr", bind_addr); + up(new Event(Event.CONFIG, m)); + } + } + + + public void destroy() { + if(init_count == 0) + return; + init_count=Math.max(init_count -1, 0); + if(init_count == 0) { + super.destroy(); + if(timer != null) { + try { + timer.stop(); + } + catch(InterruptedException e) { + log.error("failed stopping the timer", e); + } + } + + // 3. Stop the thread pools + if(oob_thread_pool instanceof ThreadPoolExecutor) { + shutdownThreadPool(oob_thread_pool); + oob_thread_pool=null; + } + + if(thread_pool instanceof ThreadPoolExecutor) { + shutdownThreadPool(thread_pool); + thread_pool=null; + } + } + } + + /** + * Creates the unicast and multicast sockets and starts the unicast and multicast receiver threads + */ + public void start() throws Exception { + if(timer == null) + throw new Exception("timer is null"); + + if(enable_diagnostics) { + diag_handler=new DiagnosticsHandler(); + diag_handler.start(); + for(ProbeHandler handler: preregistered_probe_handlers) + diag_handler.registerProbeHandler(handler); + preregistered_probe_handlers.clear(); + } + + if(use_incoming_packet_handler && !use_concurrent_stack) { + incoming_packet_queue=new Queue(); + incoming_packet_handler=new IncomingPacketHandler(); + incoming_packet_handler.start(); + } + + if(loopback && !use_concurrent_stack) { + incoming_msg_queue=new Queue(); + incoming_msg_handler=new IncomingMessageHandler(); + incoming_msg_handler.start(); + } + + if(enable_bundling) { + bundler=new Bundler(); + } + + setInAllThreadFactories(channel_name, local_addr, thread_naming_pattern); + sendUpLocalAddressEvent(); + } + + + public void stop() { + if(diag_handler != null) { + diag_handler.stop(); + diag_handler=null; + } + + // 1. Stop the incoming packet handler thread + if(incoming_packet_handler != null) + incoming_packet_handler.stop(); + + + // 2. Stop the incoming message handler + if(incoming_msg_handler != null) + incoming_msg_handler.stop(); + + preregistered_probe_handlers.clear(); + } + + + + protected void handleConnect() throws Exception { + connect_count++; + } + + protected void handleDisconnect() { + connect_count=Math.max(0, connect_count -1); + } + + public String getSingletonName() { + return singleton_name; + } + + + /** + * Setup the Protocol instance according to the configuration string + * @return true if no other properties are left. + * false if the properties still have data in them, ie , + * properties are left over and not handled by the protocol stack + */ + public boolean setProperties(Properties props) { + super.setProperties(props); + String str; + + try { + bind_addr=Util.getBindAddress(props); + } + catch(IOException unknown) { + throw new IllegalArgumentException("failed determining bind address", unknown); + } + + str=props.getProperty("use_local_host"); + if(str != null) { + use_local_host=Boolean.parseBoolean(str); + props.remove("use_local_host"); + } + + str=props.getProperty("bind_to_all_interfaces"); + if(str != null) { + receive_on_all_interfaces=Boolean.parseBoolean(str); + props.remove("bind_to_all_interfaces"); + log.warn("bind_to_all_interfaces has been deprecated; use receive_on_all_interfaces instead"); + } + + str=props.getProperty("receive_on_all_interfaces"); + if(str != null) { + receive_on_all_interfaces=Boolean.parseBoolean(str); + props.remove("receive_on_all_interfaces"); + } + + str=props.getProperty("receive_interfaces"); + if(str != null) { + try { + receive_interfaces=Util.parseInterfaceList(str); + props.remove("receive_interfaces"); + } + catch(Exception e) { + log.error("error determining interfaces (" + str + ")", e); + return false; + } + } + + str=props.getProperty("send_on_all_interfaces"); + if(str != null) { + send_on_all_interfaces=Boolean.parseBoolean(str); + props.remove("send_on_all_interfaces"); + } + + str=props.getProperty("send_interfaces"); + if(str != null) { + try { + send_interfaces=Util.parseInterfaceList(str); + props.remove("send_interfaces"); + } + catch(Exception e) { + log.error("error determining interfaces (" + str + ")", e); + return false; + } + } + + str=props.getProperty("bind_port"); + if(str != null) { + bind_port=Integer.parseInt(str); + props.remove("bind_port"); + } + + str=props.getProperty("port_range"); + if(str != null) { + port_range=Integer.parseInt(str); + props.remove("port_range"); + } + + str=props.getProperty("persistent_ports_file"); + if(str != null) { + persistent_ports_file=str; + props.remove("persistent_ports_file"); + } + + str=props.getProperty("ports_expiry_time"); + if(str != null) { + pm_expiry_time=Integer.parseInt(str); + if(pm != null) + pm.setExpiryTime(pm_expiry_time); + props.remove("ports_expiry_time"); + } + + str=props.getProperty("persistent_ports"); + if(str != null) { + if(Boolean.valueOf(str).booleanValue()) + pm=new PortsManager(pm_expiry_time, persistent_ports_file); + props.remove("persistent_ports"); + } + + str=props.getProperty("loopback"); + if(str != null) { + loopback=Boolean.valueOf(str).booleanValue(); + props.remove("loopback"); + } + + str=props.getProperty("discard_incompatible_packets"); + if(str != null) { + discard_incompatible_packets=Boolean.valueOf(str).booleanValue(); + props.remove("discard_incompatible_packets"); + } + + // this is deprecated, just left for compatibility (use use_incoming_packet_handler) + str=props.getProperty("use_packet_handler"); + if(str != null) { + use_incoming_packet_handler=Boolean.valueOf(str).booleanValue(); + props.remove("use_packet_handler"); + if(log.isWarnEnabled()) log.warn("'use_packet_handler' is deprecated; use 'use_incoming_packet_handler' instead"); + } + + str=props.getProperty("use_incoming_packet_handler"); + if(str != null) { + use_incoming_packet_handler=Boolean.valueOf(str).booleanValue(); + props.remove("use_incoming_packet_handler"); + } + + str=props.getProperty("use_concurrent_stack"); + if(str != null) { + use_concurrent_stack=Boolean.valueOf(str).booleanValue(); + props.remove("use_concurrent_stack"); + } + + str=props.getProperty("thread_naming_pattern"); + if(str != null) { + thread_naming_pattern=str; + props.remove("thread_naming_pattern"); + } + + // ======================================= OOB thread pool ========================================= + str=props.getProperty("oob_thread_pool.enabled"); + if(str != null) { + oob_thread_pool_enabled=Boolean.valueOf(str).booleanValue(); + props.remove("oob_thread_pool.enabled"); + } + + str=props.getProperty("oob_thread_pool.min_threads"); + if(str != null) { + oob_thread_pool_min_threads=Integer.valueOf(str).intValue(); + props.remove("oob_thread_pool.min_threads"); + } + + str=props.getProperty("oob_thread_pool.max_threads"); + if(str != null) { + oob_thread_pool_max_threads=Integer.valueOf(str).intValue(); + props.remove("oob_thread_pool.max_threads"); + } + + str=props.getProperty("oob_thread_pool.keep_alive_time"); + if(str != null) { + oob_thread_pool_keep_alive_time=Long.valueOf(str).longValue(); + props.remove("oob_thread_pool.keep_alive_time"); + } + + str=props.getProperty("oob_thread_pool.queue_enabled"); + if(str != null) { + oob_thread_pool_queue_enabled=Boolean.valueOf(str).booleanValue(); + props.remove("oob_thread_pool.queue_enabled"); + } + + str=props.getProperty("oob_thread_pool.queue_max_size"); + if(str != null) { + oob_thread_pool_queue_max_size=Integer.valueOf(str).intValue(); + props.remove("oob_thread_pool.queue_max_size"); + } + + str=props.getProperty("oob_thread_pool.rejection_policy"); + if(str != null) { + str=str.toLowerCase().trim(); + oob_thread_pool_rejection_policy=str; + if(!(str.equals("run") || str.equals("abort")|| str.equals("discard")|| str.equals("discardoldest"))) { + log.error("rejection policy of " + str + " is unknown"); + return false; + } + props.remove("oob_thread_pool.rejection_policy"); + } + + + + + // ======================================= Regular thread pool ========================================= + str=props.getProperty("thread_pool.enabled"); + if(str != null) { + thread_pool_enabled=Boolean.valueOf(str).booleanValue(); + props.remove("thread_pool.enabled"); + } + + str=props.getProperty("thread_pool.min_threads"); + if(str != null) { + thread_pool_min_threads=Integer.valueOf(str).intValue(); + props.remove("thread_pool.min_threads"); + } + + str=props.getProperty("thread_pool.max_threads"); + if(str != null) { + thread_pool_max_threads=Integer.valueOf(str).intValue(); + props.remove("thread_pool.max_threads"); + } + + str=props.getProperty("thread_pool.keep_alive_time"); + if(str != null) { + thread_pool_keep_alive_time=Long.valueOf(str).longValue(); + props.remove("thread_pool.keep_alive_time"); + } + + str=props.getProperty("thread_pool.queue_enabled"); + if(str != null) { + thread_pool_queue_enabled=Boolean.valueOf(str).booleanValue(); + props.remove("thread_pool.queue_enabled"); + } + + str=props.getProperty("thread_pool.queue_max_size"); + if(str != null) { + thread_pool_queue_max_size=Integer.valueOf(str).intValue(); + props.remove("thread_pool.queue_max_size"); + } + + str=props.getProperty("thread_pool.rejection_policy"); + if(str != null) { + str=str.toLowerCase().trim(); + thread_pool_rejection_policy=str; + if(!(str.equals("run") || str.equals("abort")|| str.equals("discard")|| str.equals("discardoldest"))) { + log.error("rejection policy of " + str + " is unknown"); + return false; + } + props.remove("thread_pool.rejection_policy"); + } + + + str=props.getProperty("use_outgoing_packet_handler"); + if(str != null) { + log.warn("Attribute \"use_outgoing_packet_handler\" has been deprecated and is ignored"); + props.remove("use_outgoing_packet_handler"); + } + + str=props.getProperty("outgoing_queue_max_size"); + if(str != null) { + log.warn("Attribute \"use_outgoing_queue_max_size\" has been deprecated and is ignored"); + props.remove("outgoing_queue_max_size"); + } + + str=props.getProperty("max_bundle_size"); + if(str != null) { + int bundle_size=Integer.parseInt(str); + if(bundle_size <= 0) { + if(log.isErrorEnabled()) log.error("max_bundle_size (" + bundle_size + ") is <= 0"); + return false; + } + max_bundle_size=bundle_size; + props.remove("max_bundle_size"); + } + + str=props.getProperty("max_bundle_timeout"); + if(str != null) { + max_bundle_timeout=Long.parseLong(str); + if(max_bundle_timeout <= 0) { + if(log.isErrorEnabled()) log.error("max_bundle_timeout of " + max_bundle_timeout + " is invalid"); + return false; + } + props.remove("max_bundle_timeout"); + } + + str=props.getProperty("enable_bundling"); + if(str != null) { + enable_bundling=Boolean.valueOf(str).booleanValue(); + props.remove("enable_bundling"); + } + + str=props.getProperty("enable_unicast_bundling"); + if(str != null) { + enable_unicast_bundling=Boolean.valueOf(str).booleanValue(); + props.remove("enable_unicast_bundling"); + } + + str=props.getProperty("enable_diagnostics"); + if(str != null) { + enable_diagnostics=Boolean.valueOf(str).booleanValue(); + props.remove("enable_diagnostics"); + } + + str=props.getProperty("diagnostics_addr"); + if(str != null) { + diagnostics_addr=str; + props.remove("diagnostics_addr"); + } + + str=props.getProperty("diagnostics_port"); + if(str != null) { + diagnostics_port=Integer.parseInt(str); + props.remove("diagnostics_port"); + } + + str=props.getProperty("timer.max_threads"); + if(str != null) { + num_timer_threads=Integer.parseInt(str); + props.remove("timer.max_threads"); + log.warn("timer.max_threads is deprecated; use timer.num_threads instead"); + } + + str=props.getProperty("timer.num_threads"); + if(str != null) { + num_timer_threads=Integer.parseInt(str); + props.remove("timer.num_threads"); + } + + str=props.getProperty(Global.SINGLETON_NAME); + if(str != null) { + singleton_name=str; + props.remove(Global.SINGLETON_NAME); + } + + return true; + } + + + + + /** + * handle the UP event. + * @param evt - the event being send from the stack + */ + public Object up(Event evt) { + switch(evt.getType()) { + case Event.CONFIG: + if(singleton_name != null) + passToAllUpProtocols(evt); + else + up_prot.up(evt); + if(log.isDebugEnabled()) log.debug("received CONFIG event: " + evt.getArg()); + handleConfigEvent((Map)evt.getArg()); + return null; + } + if(singleton_name != null) { + passToAllUpProtocols(evt); + return null; + } + else + return up_prot.up(evt); + } + + /** + * Caller by the layer above this layer. Usually we just put this Message + * into the send queue and let one or more worker threads handle it. A worker thread + * then removes the Message from the send queue, performs a conversion and adds the + * modified Message to the send queue of the layer below it, by calling down()). + */ + public Object down(Event evt) { + if(evt.getType() != Event.MSG) { // unless it is a message handle it and respond + return handleDownEvent(evt); + } + + Message msg=(Message)evt.getArg(); + if(header != null) { + // added patch by Roland Kurmann (March 20 2003) + // msg.putHeader(name, new TpHeader(channel_name)); + msg.putHeaderIfAbsent(name, header); + } + + setSourceAddress(msg); // very important !! listToBuffer() will fail with a null src address !! + if(log.isTraceEnabled()) { + log.trace("sending msg to " + msg.getDest() + ", src=" + msg.getSrc() + ", headers are " + msg.printHeaders()); + } + + // Don't send if destination is local address. Instead, switch dst and src and put in up_queue. + // If multicast message, loopback a copy directly to us (but still multicast). Once we receive this, + // we will discard our own multicast message + Address dest=msg.getDest(); + boolean multicast=dest == null || dest.isMulticastAddress(); + if(loopback && (multicast || dest.equals(local_addr))) { + + // we *have* to make a copy, or else up_prot.up() might remove headers from msg which will then *not* + // be available for marshalling further down (when sending the message) + final Message copy=msg.copy(); + if(log.isTraceEnabled()) log.trace(new StringBuilder("looping back message ").append(copy)); + // up_prot.up(new Event(Event.MSG, copy)); + + // changed to fix http://jira.jboss.com/jira/browse/JGRP-506 + Executor pool=msg.isFlagSet(Message.OOB)? oob_thread_pool : thread_pool; + pool.execute(new Runnable() { + public void run() { + passMessageUp(copy, false); + } + }); + + if(!multicast) + return null; + } + + try { + send(msg, dest, multicast); + } + catch(InterruptedException interruptedEx) { + Thread.currentThread().interrupt(); // let someone else handle the interrupt + } + catch(Throwable e) { + if(log.isErrorEnabled()) { + String dst=msg.getDest() == null? "null" : msg.getDest().toString(); + log.error("failed sending message to " + dst + " (" + msg.size() + " bytes)", e); + } + } + return null; + } + + + + /*--------------------------- End of Protocol interface -------------------------- */ + + + /* ------------------------------ Private Methods -------------------------------- */ + + + + /** + * If the sender is null, set our own address. We cannot just go ahead and set the address + * anyway, as we might be sending a message on behalf of someone else ! E.gin case of + * retransmission, when the original sender has crashed, or in a FLUSH protocol when we + * have to return all unstable messages with the FLUSH_OK response. + */ + private void setSourceAddress(Message msg) { + if(msg.getSrc() == null) + msg.setSrc(local_addr); + } + + + private void passMessageUp(Message msg, boolean perform_cluster_name_matching) { + TpHeader hdr=(TpHeader)msg.getHeader(name); // replaced removeHeader() with getHeader() + if(hdr == null) { + if(channel_name == null) { + Event evt=new Event(Event.MSG, msg); + if(singleton_name != null) { + passMessageToAll(evt); + } + else { + up_prot.up(evt); + } + } + else { + if(log.isErrorEnabled()) + log.error(new StringBuilder("message does not have a transport header, msg is ").append(msg). + append(", headers are ").append(msg.printHeaders()).append(", will be discarded")); + } + return; + } + + String ch_name=hdr.channel_name; + if(singleton_name != null) { + Protocol tmp_prot=up_prots.get(ch_name); + if(tmp_prot != null) { + Event evt=new Event(Event.MSG, msg); + if(log.isTraceEnabled()) { + StringBuilder sb=new StringBuilder("message is ").append(msg).append(", headers are ").append(msg.printHeaders()); + log.trace(sb); + } + tmp_prot.up(evt); + } + else { + // we discard messages for a group we don't have. If we had a scenario with channel C1 and A,B on it, + // and channel C2 and only A on it (asymmetric setup), then C2 would always log warnings that B was + // not found (Jan 25 2008 (bela)) + // if(log.isWarnEnabled()) + // log.warn(new StringBuilder("discarded message from group \"").append(ch_name). + // append("\" (our groups are ").append(up_prots.keySet()).append("). Sender was ").append(msg.getSrc())); + } + } + else { + // Discard if message's group name is not the same as our group name + if(perform_cluster_name_matching && channel_name != null && !channel_name.equals(ch_name)) { + if(log.isWarnEnabled()) + log.warn(new StringBuilder("discarded message from different group \"").append(ch_name). + append("\" (our group is \"").append(channel_name).append("\"). Sender was ").append(msg.getSrc())); + } + else { + Event evt=new Event(Event.MSG, msg); + if(log.isTraceEnabled()) { + StringBuilder sb=new StringBuilder("message is ").append(msg).append(", headers are ").append(msg.printHeaders()); + log.trace(sb); + } + up_prot.up(evt); + } + } + } + + private void passMessageToAll(Event evt) { + for(Protocol tmp_prot: up_prots.values()) { + try { + tmp_prot.up(evt); + } + catch(Exception ex) { + if(log.isErrorEnabled()) + log.error("failure passing message up: message is " + evt.getArg(), ex); + } + } + } + + + /** + * Subclasses must call this method when a unicast or multicast message has been received. + * Declared final so subclasses cannot override this method. + * + * @param dest + * @param sender + * @param data + * @param offset + * @param length + */ + protected final void receive(Address dest, Address sender, byte[] data, int offset, int length) { + if(data == null) return; + + if(log.isTraceEnabled()){ + boolean mcast=dest == null || dest.isMulticastAddress(); + StringBuilder sb=new StringBuilder("received ("); + sb.append(mcast? "mcast) " : "ucast) ").append(length).append(" bytes from ").append(sender); + log.trace(sb); + } + + try { + // determine whether OOB or not by looking at first byte of 'data' + boolean oob=false; + byte oob_flag=data[Global.SHORT_SIZE]; // we need to skip the first 2 bytes (version) + if((oob_flag & OOB) == OOB) + oob=true; + + if(use_concurrent_stack) { + if(oob) { + num_oob_msgs_received++; + dispatchToThreadPool(oob_thread_pool, dest, sender, data, offset, length); + } + else { + num_incoming_msgs_received++; + dispatchToThreadPool(thread_pool, dest, sender, data, offset, length); + } + } + else { + if(use_incoming_packet_handler) { + byte[] tmp=new byte[length]; + System.arraycopy(data, offset, tmp, 0, length); + incoming_packet_queue.add(new IncomingPacket(dest, sender, tmp, 0, length)); + } + else + handleIncomingPacket(dest, sender, data, offset, length); + } + } + catch(Throwable t) { + if(log.isErrorEnabled()) + log.error(new StringBuilder("failed handling data from ").append(sender), t); + } + } + + + + private void dispatchToThreadPool(Executor pool, Address dest, Address sender, byte[] data, int offset, int length) { + if(pool instanceof DirectExecutor) { + // we don't make a copy of the buffer if we execute on this thread + pool.execute(new IncomingPacket(dest, sender, data, offset, length)); + } + else { + byte[] tmp=new byte[length]; + System.arraycopy(data, offset, tmp, 0, length); + pool.execute(new IncomingPacket(dest, sender, tmp, 0, length)); + } + } + + + /** + * Processes a packet read from either the multicast or unicast socket. Needs to be synchronized because + * mcast or unicast socket reads can be concurrent. + * Correction (bela April 19 2005): we access no instance variables, all vars are allocated on the stack, so + * this method should be reentrant: removed 'synchronized' keyword + */ + private void handleIncomingPacket(Address dest, Address sender, byte[] data, int offset, int length) { + Message msg=null; + short version=0; + boolean is_message_list, multicast; + byte flags; + List msgs; + + try { + synchronized(in_stream) { + in_stream.setData(data, offset, length); + try { + version=dis.readShort(); + } + catch(IOException ex) { + if(discard_incompatible_packets) + return; + throw ex; + } + if(Version.isBinaryCompatible(version) == false) { + if(log.isWarnEnabled()) { + StringBuilder sb=new StringBuilder(); + sb.append("packet from ").append(sender).append(" has different version (").append(Version.print(version)); + sb.append(") from ours (").append(Version.printVersion()).append("). "); + if(discard_incompatible_packets) + sb.append("Packet is discarded"); + else + sb.append("This may cause problems"); + log.warn(sb); + } + if(discard_incompatible_packets) + return; + } + + flags=dis.readByte(); + is_message_list=(flags & LIST) == LIST; + multicast=(flags & MULTICAST) == MULTICAST; + + if(is_message_list) + msgs=readMessageList(dis, dest, multicast); + else { + msg=readMessage(dis, dest, sender, multicast); + msgs=new LinkedList(); + msgs.add(msg); + } + } + + Address src; + for(Iterator it=msgs.iterator(); it.hasNext();) { + msg=it.next(); + src=msg.getSrc(); + if(loopback) { + if(multicast && src != null && src.equals(local_addr)) { // discard own loopback multicast packets + it.remove(); + } + } + else + handleIncomingMessage(msg); + } + if(incoming_msg_queue != null && !msgs.isEmpty()) + incoming_msg_queue.addAll(msgs); + } + catch(Throwable t) { + if(log.isErrorEnabled()) + log.error("failed unmarshalling message", t); + } + } + + + + private void handleIncomingMessage(Message msg) { + if(stats) { + num_msgs_received++; + num_bytes_received+=msg.getLength(); + } + passMessageUp(msg, true); + } + + + /** Internal method to serialize and send a message. This method is not reentrant */ + private void send(Message msg, Address dest, boolean multicast) throws Exception { + + // bundle only regular messages; send OOB messages directly + if(enable_bundling && !msg.isFlagSet(Message.OOB)) { + if(!enable_unicast_bundling && !multicast) { + ; // don't bundle unicast msgs if enable_unicast_bundling is off (http://jira.jboss.com/jira/browse/JGRP-429) + } + else { + bundler.send(msg, dest); + return; + } + } + + ExposedByteArrayOutputStream out_stream=null; + ExposedDataOutputStream dos=null; + Buffer buf; + out_stream=new ExposedByteArrayOutputStream(INITIAL_BUFSIZE); + dos=new ExposedDataOutputStream(out_stream); + writeMessage(msg, dos, multicast); + buf=new Buffer(out_stream.getRawBuffer(), 0, out_stream.size()); + doSend(buf, dest, multicast); + } + + + private void doSend(Buffer buf, Address dest, boolean multicast) throws Exception { + if(stats) { + num_msgs_sent++; + num_bytes_sent+=buf.getLength(); + } + if(multicast) { + sendToAllMembers(buf.getBuf(), buf.getOffset(), buf.getLength()); + } + else { + sendToSingleMember(dest, buf.getBuf(), buf.getOffset(), buf.getLength()); + } + } + + + + /** + * This method needs to be synchronized on out_stream when it is called + * @param msg + * @return + * @throws java.io.IOException + */ + private static void writeMessage(Message msg, DataOutputStream dos, boolean multicast) throws Exception { + byte flags=0; + dos.writeShort(Version.version); // write the version + if(multicast) + flags+=MULTICAST; + if(msg.isFlagSet(Message.OOB)) + flags+=OOB; + dos.writeByte(flags); + msg.writeTo(dos); + } + + private Message readMessage(DataInputStream instream, Address dest, Address sender, boolean multicast) throws Exception { + Message msg=new Message(false); // don't create headers, readFrom() will do this + msg.readFrom(instream); + postUnmarshalling(msg, dest, sender, multicast); // allows for optimization by subclass + return msg; + } + + + + private static void writeMessageList(List msgs, DataOutputStream dos, boolean multicast) throws Exception { + Address src; + byte flags=0; + int len=msgs != null? msgs.size() : 0; + boolean src_written=false; + + dos.writeShort(Version.version); + flags+=LIST; + if(multicast) + flags+=MULTICAST; + dos.writeByte(flags); + dos.writeInt(len); + if(msgs != null) { + for(Message msg: msgs) { + src=msg.getSrc(); + if(!src_written) { + Util.writeAddress(src, dos); + src_written=true; + } + msg.writeTo(dos); + } + } + } + + private List readMessageList(DataInputStream instream, Address dest, boolean multicast) throws Exception { + List list=new LinkedList(); + int len; + Message msg; + Address src; + + len=instream.readInt(); + src=Util.readAddress(instream); + for(int i=0; i < len; i++) { + msg=new Message(false); // don't create headers, readFrom() will do this + msg.readFrom(instream); + postUnmarshallingList(msg, dest, multicast); + msg.setSrc(src); + list.add(msg); + } + return list; + } + + + + protected Object handleDownEvent(Event evt) { + switch(evt.getType()) { + + case Event.TMP_VIEW: + case Event.VIEW_CHANGE: + synchronized(members) { + view=(View)evt.getArg(); + members.clear(); + + if(singleton_name == null) { + Vector
        tmpvec=view.getMembers(); + members.addAll(tmpvec); + } + else { + for(Protocol prot: up_prots.values()) { + if(prot instanceof ProtocolAdapter) { + ProtocolAdapter ad=(ProtocolAdapter)prot; + List
        tmp=ad.getMembers(); + members.addAll(tmp); + } + } + } + } + break; + + case Event.CONNECT: + case Event.CONNECT_WITH_STATE_TRANSFER: + channel_name=(String)evt.getArg(); + header=new TpHeader(channel_name); + setInAllThreadFactories(channel_name, local_addr, thread_naming_pattern); + setThreadNames(); + connectLock.lock(); + try { + handleConnect(); + } + catch(Exception e) { + throw new RuntimeException(e); + } + finally { + connectLock.unlock(); + } + return null; + + case Event.DISCONNECT: + unsetThreadNames(); + connectLock.lock(); + try { + handleDisconnect(); + } + finally { + connectLock.unlock(); + } + break; + + case Event.CONFIG: + if(log.isDebugEnabled()) log.debug("received CONFIG event: " + evt.getArg()); + handleConfigEvent((Map)evt.getArg()); + break; + } + return null; + } + + + + + protected void setThreadNames() { + if(incoming_packet_handler != null) + global_thread_factory.renameThread(IncomingPacketHandler.THREAD_NAME, incoming_packet_handler.getThread()); + + if(incoming_msg_handler != null) { + global_thread_factory.renameThread(IncomingMessageHandler.THREAD_NAME, incoming_msg_handler.getThread()); + } + + if(diag_handler != null) { + global_thread_factory.renameThread(DiagnosticsHandler.THREAD_NAME, diag_handler.getThread()); + } + } + + protected void unsetThreadNames() { + if(incoming_packet_handler != null && incoming_packet_handler.getThread() != null) + incoming_packet_handler.getThread().setName(IncomingPacketHandler.THREAD_NAME); + if(incoming_msg_handler != null && incoming_msg_handler.getThread() != null) + incoming_msg_handler.getThread().setName(IncomingMessageHandler.THREAD_NAME); + if(diag_handler != null && diag_handler.getThread() != null) + diag_handler.getThread().setName(DiagnosticsHandler.THREAD_NAME); + } + + private void setInAllThreadFactories(String cluster_name, Address local_address, String pattern) { + ThreadFactory[] factories={timer_thread_factory, default_thread_factory, oob_thread_factory, global_thread_factory}; + boolean is_shared_transport=singleton_name != null && singleton_name.trim().length() > 0; + + for(ThreadFactory factory:factories) { + if(pattern != null) { + factory.setPattern(pattern); + if(is_shared_transport) + factory.setIncludeClusterName(false); + } + if(cluster_name != null && !is_shared_transport) // only set cluster name if we don't have a shared transport + factory.setClusterName(cluster_name); + if(local_address != null) + factory.setAddress(local_address.toString()); + } + } + + + protected void handleConfigEvent(Map map) { + if(map == null) return; + if(map.containsKey("additional_data")) { + additional_data=(byte[])map.get("additional_data"); + if(local_addr instanceof IpAddress) + ((IpAddress)local_addr).setAdditionalData(additional_data); + } + } + + + protected static ExecutorService createThreadPool(int min_threads, int max_threads, long keep_alive_time, String rejection_policy, + BlockingQueue queue, final ThreadFactory factory) { + + ThreadPoolExecutor pool=new ThreadManagerThreadPoolExecutor(min_threads, max_threads, keep_alive_time, TimeUnit.MILLISECONDS, queue); + pool.setThreadFactory(factory); + + //default + RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy(); + if(rejection_policy != null) { + if(rejection_policy.equals("abort")) + handler = new ThreadPoolExecutor.AbortPolicy(); + else if(rejection_policy.equals("discard")) + handler = new ThreadPoolExecutor.DiscardPolicy(); + else if(rejection_policy.equals("discardoldest")) + handler = new ThreadPoolExecutor.DiscardOldestPolicy(); + } + pool.setRejectedExecutionHandler(new ShutdownRejectedExecutionHandler(handler)); + + return pool; + } + + + private static void shutdownThreadPool(Executor thread_pool) { + if(thread_pool instanceof ExecutorService) { + ExecutorService service=(ExecutorService)thread_pool; + service.shutdownNow(); + try { + service.awaitTermination(Global.THREADPOOL_SHUTDOWN_WAIT_TIME, TimeUnit.MILLISECONDS); + } + catch(InterruptedException e) { + } + } + } + + private void verifyRejectionPolicy(String str) throws Exception{ + if(!(str.equalsIgnoreCase("run") || str.equalsIgnoreCase("abort")|| str.equalsIgnoreCase("discard")|| str.equalsIgnoreCase("discardoldest"))) { + log.error("rejection policy of " + str + " is unknown"); + throw new Exception("Unknown rejection policy " + str); + } + } + + protected void passToAllUpProtocols(Event evt) { + for(Protocol prot: up_prots.values()) { + try { + prot.up(evt); + } + catch(Exception e) { + if(log.isErrorEnabled()) + log.error("failed passing up event " + evt, e); + } + } + } + + public void sendUpLocalAddressEvent() { + if(up_prot != null) + up(new Event(Event.SET_LOCAL_ADDRESS, local_addr)); + else { + for(Map.Entry entry: up_prots.entrySet()) { + String tmp=entry.getKey(); + if(tmp.startsWith(Global.DUMMY)) + continue; + Protocol prot=entry.getValue(); + prot.up(new Event(Event.SET_LOCAL_ADDRESS, local_addr)); + } + } + } + + /* ----------------------------- End of Private Methods ---------------------------------------- */ + + + + /* ----------------------------- Inner Classes ---------------------------------------- */ + + class IncomingPacket implements Runnable { + Address dest=null; + Address sender=null; + byte[] buf; + int offset, length; + + IncomingPacket(Address dest, Address sender, byte[] buf, int offset, int length) { + this.dest=dest; + this.sender=sender; + this.buf=buf; + this.offset=offset; + this.length=length; + } + + + /** Code copied from handleIncomingPacket */ + public void run() { + short version=0; + boolean is_message_list, multicast; + byte flags; + ExposedByteArrayInputStream in_stream=null; + DataInputStream dis=null; + + try { + in_stream=new ExposedByteArrayInputStream(buf, offset, length); + dis=new DataInputStream(in_stream); + try { + version=dis.readShort(); + } + catch(IOException ex) { + if(discard_incompatible_packets) + return; + throw ex; + } + if(Version.isBinaryCompatible(version) == false) { + if(log.isWarnEnabled()) { + StringBuilder sb=new StringBuilder(); + sb.append("packet from ").append(sender).append(" has different version (").append(Version.print(version)); + sb.append(") from ours (").append(Version.printVersion()).append("). "); + if(discard_incompatible_packets) + sb.append("Packet is discarded"); + else + sb.append("This may cause problems"); + log.warn(sb); + } + if(discard_incompatible_packets) + return; + } + + flags=dis.readByte(); + is_message_list=(flags & LIST) == LIST; + multicast=(flags & MULTICAST) == MULTICAST; + + if(is_message_list) { // used if message bundling is enabled + List msgs=readMessageList(dis, dest, multicast); + for(Message msg: msgs) { + if(msg.isFlagSet(Message.OOB)) { + log.warn("bundled message should not be marked as OOB"); + } + handleMyMessage(msg, multicast); + } + } + else { + Message msg=readMessage(dis, dest, sender, multicast); + handleMyMessage(msg, multicast); + } + } + catch(Throwable t) { + if(log.isErrorEnabled()) + log.error("failed handling incoming message", t); + } + } + + + private void handleMyMessage(Message msg, boolean multicast) { + if(stats) { + num_msgs_received++; + num_bytes_received+=msg.getLength(); + } + + Address src=msg.getSrc(); + if(loopback && multicast && src != null && src.equals(local_addr)) { + return; // drop message that was already looped back and delivered + } + + passMessageUp(msg, true); + } + } + + + + + + /** + * This thread fetches byte buffers from the packet_queue, converts them into messages and passes them up + * to the higher layer (done in handleIncomingUdpPacket()). + */ + class IncomingPacketHandler implements Runnable { + + public static final String THREAD_NAME="IncomingPacketHandler"; + Thread t=null; + + Thread getThread(){ + return t; + } + + void start() { + if(t == null || !t.isAlive()) { + t=global_thread_factory.newThread(this, THREAD_NAME); + t.setDaemon(true); + t.start(); + } + } + + void stop() { + incoming_packet_queue.close(true); // should terminate the packet_handler thread too + if(t != null) { + try { + t.join(Global.THREAD_SHUTDOWN_WAIT_TIME); + } + catch(InterruptedException e) { + Thread.currentThread().interrupt(); // set interrupt flag again + } + } + } + + public void run() { + IncomingPacket entry; + while(!incoming_packet_queue.closed() && Thread.currentThread().equals(t)) { + try { + entry=(IncomingPacket)incoming_packet_queue.remove(); + handleIncomingPacket(entry.dest, entry.sender, entry.buf, entry.offset, entry.length); + } + catch(QueueClosedException closed_ex) { + break; + } + catch(Throwable ex) { + if(log.isErrorEnabled()) + log.error("error processing incoming packet", ex); + } + } + if(log.isTraceEnabled()) log.trace("incoming packet handler terminating"); + } + } + + + class IncomingMessageHandler implements Runnable { + + public static final String THREAD_NAME = "IncomingMessageHandler"; + Thread t; + + Thread getThread(){ + return t; + } + + public void start() { + if(t == null || !t.isAlive()) { + t=global_thread_factory.newThread(this, THREAD_NAME); + t.setDaemon(true); + t.start(); + } + } + + + public void stop() { + incoming_msg_queue.close(true); + if(t != null) { + try { + t.join(Global.THREAD_SHUTDOWN_WAIT_TIME); + } + catch(InterruptedException e) { + Thread.currentThread().interrupt(); // set interrupt flag again + } + } + } + + public void run() { + Message msg; + while(!incoming_msg_queue.closed() && Thread.currentThread().equals(t)) { + try { + msg=(Message)incoming_msg_queue.remove(); + handleIncomingMessage(msg); + } + catch(QueueClosedException closed_ex) { + break; + } + catch(Throwable ex) { + if(log.isErrorEnabled()) + log.error("error processing incoming message", ex); + } + } + if(log.isTraceEnabled()) log.trace("incoming message handler terminating"); + } + } + + + + + private class Bundler { + static final int MIN_NUMBER_OF_BUNDLING_TASKS=2; + /** HashMap>. Keys are destinations, values are lists of Messages */ + final Map> msgs=new HashMap>(36); + @GuardedBy("lock") + long count=0; // current number of bytes accumulated + int num_msgs=0; + @GuardedBy("lock") + int num_bundling_tasks=0; + long last_bundle_time; + final ReentrantLock lock=new ReentrantLock(); + + + private void send(Message msg, Address dest) throws Exception { + long length=msg.size(); + checkLength(length); + Map> bundled_msgs=null; + + lock.lock(); + try { + if(count + length >= max_bundle_size) { + bundled_msgs=removeBundledMessages(); + } + addMessage(msg, dest); + count+=length; + if(num_bundling_tasks < MIN_NUMBER_OF_BUNDLING_TASKS) { + num_bundling_tasks++; + timer.schedule(new BundlingTimer(), max_bundle_timeout, TimeUnit.MILLISECONDS); + } + } + finally { + lock.unlock(); + } + + if(bundled_msgs != null) { + sendBundledMessages(bundled_msgs); + } + } + + /** Run with lock acquired */ + private void addMessage(Message msg, Address dest) { // no sync needed, always called with lock held + if(msgs.isEmpty()) + last_bundle_time=System.currentTimeMillis(); + List tmp=msgs.get(dest); + if(tmp == null) { + tmp=new LinkedList(); + msgs.put(dest, tmp); + } + tmp.add(msg); + num_msgs++; + } + + + /** Must always be called with lock held */ + private Map> removeBundledMessages() { + if(msgs.isEmpty()) + return null; + Map> copy=new HashMap>(msgs); + if(log.isTraceEnabled()) { + long stop=System.currentTimeMillis(); + double percentage=100.0 / max_bundle_size * count; + StringBuilder sb=new StringBuilder("sending ").append(num_msgs).append(" msgs ("); + num_msgs=0; + sb.append(count).append(" bytes (" + f.format(percentage) + "% of max_bundle_size)"); + if(last_bundle_time > 0) { + sb.append(", collected in ").append(stop-last_bundle_time).append("ms) "); + } + sb.append(" to ").append(copy.size()).append(" destination(s)"); + if(copy.size() > 1) sb.append(" (dests=").append(copy.keySet()).append(")"); + log.trace(sb); + } + msgs.clear(); + count=0; + return copy; + } + + + /** + * Sends all messages from the map, all messages for the same destination are bundled into 1 message. + * This method may be called by timer and bundler concurrently + * @param msgs + */ + private void sendBundledMessages(Map> msgs) { + boolean multicast; + Buffer buffer; + Map.Entry> entry; + Address dst; + ExposedByteArrayOutputStream out_stream=new ExposedByteArrayOutputStream(INITIAL_BUFSIZE); + ExposedDataOutputStream dos=new ExposedDataOutputStream(out_stream); + boolean first=true; + + for(Iterator>> it=msgs.entrySet().iterator(); it.hasNext();) { + entry=it.next(); + List list=entry.getValue(); + if(list.isEmpty()) + continue; + dst=entry.getKey(); + multicast=dst == null || dst.isMulticastAddress(); + try { + if(first) { + first=false; + } + else { + out_stream.reset(); + dos.reset(); + } + writeMessageList(list, dos, multicast); // flushes output stream when done + buffer=new Buffer(out_stream.getRawBuffer(), 0, out_stream.size()); + doSend(buffer, dst, multicast); + } + catch(Throwable e) { + if(log.isErrorEnabled()) log.error("exception sending msg: " + e.toString(), e.getCause()); + } + } + } + + + + private void checkLength(long len) throws Exception { + if(len > max_bundle_size) + throw new Exception("message size (" + len + ") is greater than max bundling size (" + max_bundle_size + + "). Set the fragmentation/bundle size in FRAG and TP correctly"); + } + + + private class BundlingTimer implements Runnable { + + public void run() { + Map> msgs=null; + boolean unlocked=false; + + lock.lock(); + try { + msgs=removeBundledMessages(); + if(msgs != null) { + lock.unlock(); + unlocked=true; + sendBundledMessages(msgs); + } + } + finally { + if(unlocked) + lock.lock(); + num_bundling_tasks--; + lock.unlock(); + } + } + } + } + + public interface ProbeHandler { + /** + * Handles a probe. For each key that is handled, the key and its result should be in the returned map. + * @param keys + * @return Map. A map of keys and values. A null return value is permissible. + */ + Map handleProbe(String... keys); + + /** Returns a list of supported keys */ + String[] supportedKeys(); + } + + + private class DiagnosticsHandler implements Runnable { + public static final String THREAD_NAME = "DiagnosticsHandler"; + private Thread thread=null; + private MulticastSocket diag_sock=null; + private final Set handlers=new HashSet(); + + DiagnosticsHandler() { + } + + Thread getThread(){ + return thread; + } + + void registerProbeHandler(ProbeHandler handler) { + if(handler != null) + handlers.add(handler); + } + + void unregisterProbeHandler(ProbeHandler handler) { + if(handler != null) + handlers.remove(handler); + } + + void start() throws IOException { + + registerProbeHandler(new ProbeHandler() { + + public Map handleProbe(String... keys) { + Map retval=new HashMap(2); + for(String key: keys) { + if(key.equals("dump")) { + retval.put("dump", Util.dumpThreads()); + continue; + } + if(key.equals("keys")) { + StringBuilder sb=new StringBuilder(); + for(ProbeHandler handler: handlers) { + String[] tmp=handler.supportedKeys(); + if(tmp != null && tmp.length > 0) { + for(String s: tmp) + sb.append(s).append(" "); + } + } + retval.put("keys", sb.toString()); + } + } + return retval; + } + + public String[] supportedKeys() { + return new String[]{"dump", "keys"}; + } + }); + + diag_sock=new MulticastSocket(diagnostics_port); + java.util.List interfaces=Util.getAllAvailableInterfaces(); + bindToInterfaces(interfaces, diag_sock); + + if(thread == null || !thread.isAlive()) { + thread=global_thread_factory.newThread(this, THREAD_NAME); + thread.setDaemon(true); + thread.start(); + } + } + + void stop() { + if(diag_sock != null) + diag_sock.close(); + handlers.clear(); + if(thread != null){ + try{ + thread.join(Global.THREAD_SHUTDOWN_WAIT_TIME); + } + catch(InterruptedException e){ + Thread.currentThread().interrupt(); // set interrupt flag + } + } + } + + public void run() { + byte[] buf=new byte[1500]; // MTU on most LANs + DatagramPacket packet; + while(!diag_sock.isClosed() && Thread.currentThread().equals(thread)) { + packet=new DatagramPacket(buf, 0, buf.length); + try { + diag_sock.receive(packet); + handleDiagnosticProbe(packet.getSocketAddress(), diag_sock, + new String(packet.getData(), packet.getOffset(), packet.getLength())); + } + catch(IOException e) { + } + } + } + + private void handleDiagnosticProbe(SocketAddress sender, DatagramSocket sock, String request) { + StringTokenizer tok=new StringTokenizer(request); + List list=new ArrayList(10); + + while(tok.hasMoreTokens()) { + String req=tok.nextToken().trim(); + if(req.length() > 0) + list.add(req); + } + + String[] tokens=new String[list.size()]; + for(int i=0; i < list.size(); i++) + tokens[i]=list.get(i); + + + for(ProbeHandler handler: handlers) { + Map map=handler.handleProbe(tokens); + if(map == null || map.isEmpty()) + continue; + if(!map.containsKey("local_addr")) + map.put("local_addr", local_addr != null? local_addr.toString() : "n/a"); + if(!map.containsKey("cluster")) + map.put("cluster", channel_name != null? channel_name : "n/a"); + StringBuilder info=new StringBuilder(); + for(Map.Entry entry: map.entrySet()) { + info.append(entry.getKey()).append("=").append(entry.getValue()).append("\r\n"); + } + + byte[] diag_rsp=info.toString().getBytes(); + if(log.isDebugEnabled()) + log.debug("sending diag response to " + sender); + try { + sendResponse(sock, sender, diag_rsp); + } + catch(Throwable t) { + if(log.isErrorEnabled()) + log.error("failed sending diag rsp to " + sender, t); + } + } + } + + private void sendResponse(DatagramSocket sock, SocketAddress sender, byte[] buf) throws IOException { + DatagramPacket p=new DatagramPacket(buf, 0, buf.length, sender); + sock.send(p); + } + + private void bindToInterfaces(java.util.List interfaces, MulticastSocket s) { + SocketAddress group_addr=new InetSocketAddress(diagnostics_addr, diagnostics_port); + for(Iterator it=interfaces.iterator(); it.hasNext();) { + NetworkInterface i=it.next(); + try { + if (i.getInetAddresses().hasMoreElements()) { // fix for VM crash - suggested by JJalenak@netopia.com + s.joinGroup(group_addr, i); + if(log.isTraceEnabled()) + log.trace("joined " + group_addr + " on " + i.getName()); + } + } + catch(IOException e) { + log.warn("failed to join " + group_addr + " on " + i.getName() + ": " + e); + } + } + } + } + + public static class ProtocolAdapter extends Protocol { + final String cluster_name; + final String transport_name; + final TpHeader header; + final List
        members=new CopyOnWriteArrayList
        (); + final ThreadFactory factory; + + public ProtocolAdapter(String cluster_name, String transport_name, Protocol up, Protocol down, String pattern, Address addr) { + this.cluster_name=cluster_name; + this.transport_name=transport_name; + this.up_prot=up; + this.down_prot=down; + this.header=new TpHeader(cluster_name); + this.factory=new DefaultThreadFactory(Util.getGlobalThreadGroup(), "", false); + factory.setPattern(pattern); + if(addr != null) + factory.setAddress(addr.toString()); +} + + public List
        getMembers() { + return Collections.unmodifiableList(members); + } + + public ThreadFactory getThreadFactory() { + return factory; + } + + public Object down(Event evt) { + switch(evt.getType()) { + case Event.MSG: + Message msg=(Message)evt.getArg(); + msg.putHeader(transport_name, header); + break; + case Event.VIEW_CHANGE: + View view=(View)evt.getArg(); + Vector
        tmp=view.getMembers(); + members.clear(); + members.addAll(tmp); + break; + case Event.CONNECT: + case Event.CONNECT_WITH_STATE_TRANSFER: + factory.setClusterName((String)evt.getArg()); + break; + } + return down_prot.down(evt); + } + + public Object up(Event evt) { + switch(evt.getType()) { + case Event.SET_LOCAL_ADDRESS: + Address addr=(Address)evt.getArg(); + if(addr != null) + factory.setAddress(addr.toString()); + break; + } + return up_prot.up(evt); + } + + public String getName() { + return "TP.ProtocolAdapter"; + } + + public String toString() { + return cluster_name + " (" + transport_name + ")"; + } + } +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/TRACE.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/TRACE.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/TRACE.java 17 Aug 2012 14:51:05 -0000 1.1 @@ -0,0 +1,38 @@ +// $Id: TRACE.java,v 1.1 2012/08/17 14:51:05 marcin Exp $ + +package org.jgroups.protocols; +import org.jgroups.Event; +import org.jgroups.stack.Protocol; + + + +public class TRACE extends Protocol { + + public TRACE() {} + + public String getName() {return "TRACE";} + + + + public Object up(Event evt) { + System.out.println("---------------- TRACE (received) ----------------------"); + System.out.println(evt); + System.out.println("--------------------------------------------------------"); + return up_prot.up(evt); + } + + + public Object down(Event evt) { + System.out.println("------------------- TRACE (sent) -----------------------"); + System.out.println(evt); + System.out.println("--------------------------------------------------------"); + return down_prot.down(evt); + } + + + public String toString() { + return "Protocol TRACE"; + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/TUNNEL.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/TUNNEL.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/TUNNEL.java 17 Aug 2012 14:51:07 -0000 1.1 @@ -0,0 +1,315 @@ +// $Id: TUNNEL.java,v 1.1 2012/08/17 14:51:07 marcin Exp $ + +package org.jgroups.protocols; + +import org.jgroups.Address; +import org.jgroups.Event; +import org.jgroups.Message; +import org.jgroups.annotations.GuardedBy; +import org.jgroups.stack.RouterStub; +import org.jgroups.stack.IpAddress; +import org.jgroups.util.Util; + +import java.io.DataInputStream; +import java.io.IOException; +import java.net.SocketException; +import java.util.Enumeration; +import java.util.Properties; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +/** + * Replacement for UDP. Instead of sending packets via UDP, a TCP connection is + * opened to a Router (using the RouterStub client-side stub), the IP + * address/port of which was given using channel properties + * router_host and router_port. All outgoing + * traffic is sent via this TCP socket to the Router which distributes it to all + * connected TUNNELs in this group. Incoming traffic received from Router will + * simply be passed up the stack. + * + *

        + * A TUNNEL layer can be used to penetrate a firewall, most firewalls allow + * creating TCP connections to the outside world, however, they do not permit + * outside hosts to initiate a TCP connection to a host inside the firewall. + * Therefore, the connection created by the inside host is reused by Router to + * send traffic from an outside host to a host inside the firewall. + * + * @author Bela Ban + */ +public class TUNNEL extends TP { + private String router_host = null; + + private int router_port = 0; + + private RouterStub stub; + + long reconnect_interval = 5000; + + /* + * flag indicating if tunnel was destroyed intentionally (disconnect, channel destroy etc) + */ + private volatile boolean intentionallyTornDown = false; + + /** time to wait in ms between reconnect attempts */ + + @GuardedBy("reconnectorLock") + private Future reconnectorFuture = null; + + private final Lock reconnectorLock = new ReentrantLock(); + + public TUNNEL(){} + + public String toString() { + return "Protocol TUNNEL(local_addr=" + local_addr + ')'; + } + + public String getRouterHost() { + return router_host; + } + + public void setRouterHost(String router_host) { + this.router_host=router_host; + } + + public int getRouterPort() { + return router_port; + } + + public void setRouterPort(int router_port) { + this.router_port=router_port; + } + + public long getReconnectInterval() { + return reconnect_interval; + } + + public void setReconnectInterval(long reconnect_interval) { + this.reconnect_interval=reconnect_interval; + } + + /*------------------------------ Protocol interface ------------------------------ */ + + public String getName() { + return "TUNNEL"; + } + + public void init() throws Exception { + super.init(); + if(timer == null) + throw new Exception("TUNNEL.init(): timer cannot be retrieved from protocol stack"); + } + + public void start() throws Exception { + // loopback turned on is mandatory + loopback = true; + intentionallyTornDown = false; + + stub = new RouterStub(router_host, router_port, bind_addr); + stub.setConnectionListener(new StubConnectionListener()); + local_addr = stub.getLocalAddress(); + if(additional_data != null && local_addr instanceof IpAddress) + ((IpAddress) local_addr).setAdditionalData(additional_data); + super.start(); + } + + public void stop() { + teardownTunnel(); + super.stop(); + } + + /** Setup the Protocol instance acording to the configuration string */ + public boolean setProperties(Properties props) { + String str; + + super.setProperties(props); + str = props.getProperty("router_host"); + if(str != null){ + router_host = str; + props.remove("router_host"); + } + + str = props.getProperty("router_port"); + if(str != null){ + router_port = Integer.parseInt(str); + props.remove("router_port"); + } + + if(log.isDebugEnabled()){ + log.debug("router_host=" + router_host + ";router_port=" + router_port); + } + + if(router_host == null || router_port == 0){ + if(log.isErrorEnabled()){ + log.error("both router_host and router_port have to be set !"); + return false; + } + } + + str = props.getProperty("reconnect_interval"); + if(str != null){ + reconnect_interval = Long.parseLong(str); + props.remove("reconnect_interval"); + } + + if(!props.isEmpty()){ + StringBuilder sb = new StringBuilder(); + for(Enumeration e = props.propertyNames();e.hasMoreElements();){ + sb.append(e.nextElement().toString()); + if(e.hasMoreElements()){ + sb.append(", "); + } + } + if(log.isErrorEnabled()) + log.error("The following properties are not recognized: " + sb); + return false; + } + return true; + } + + /** Tears the TCP connection to the router down */ + void teardownTunnel() { + intentionallyTornDown = true; + stopReconnecting(); + stub.disconnect(); + } + + public Object handleDownEvent(Event evt) { + Object retEvent = super.handleDownEvent(evt); + switch(evt.getType()){ + case Event.CONNECT: + case Event.CONNECT_WITH_STATE_TRANSFER: + try{ + stub.connect(channel_name); + }catch(Exception e){ + if(log.isErrorEnabled()) + log.error("failed connecting to GossipRouter at " + router_host + + ":" + + router_port); + startReconnecting(); + } + break; + + case Event.DISCONNECT: + teardownTunnel(); + break; + } + return retEvent; + } + + private void startReconnecting() { + reconnectorLock.lock(); + try{ + if(reconnectorFuture == null || reconnectorFuture.isDone()){ + final Runnable reconnector = new Runnable() { + public void run() { + try{ + if(!intentionallyTornDown){ + if(log.isDebugEnabled()){ + log.debug("Reconnecting " + getLocalAddress() + + " to router at " + + router_host + + ":" + + router_port); + } + stub.connect(channel_name); + } + }catch(Exception ex){ + if(log.isTraceEnabled()) + log.trace("failed reconnecting", ex); + } + } + }; + reconnectorFuture = timer.scheduleWithFixedDelay(reconnector, + 0, + reconnect_interval, + TimeUnit.MILLISECONDS); + } + }finally{ + reconnectorLock.unlock(); + } + } + + private void stopReconnecting() { + reconnectorLock.lock(); + try{ + if(reconnectorFuture != null){ + reconnectorFuture.cancel(true); + reconnectorFuture = null; + } + }finally{ + reconnectorLock.unlock(); + } + } + + private class StubConnectionListener implements RouterStub.ConnectionListener { + + private volatile int currentState = RouterStub.STATUS_DISCONNECTED; + + public void connectionStatusChange(int newState) { + if(newState == RouterStub.STATUS_DISCONNECTED){ + startReconnecting(); + }else if(currentState != RouterStub.STATUS_CONNECTED && newState == RouterStub.STATUS_CONNECTED){ + stopReconnecting(); + Thread t = global_thread_factory.newThread(new TunnelReceiver(), "TUNNEL receiver"); + t.setDaemon(true); + t.start(); + } + currentState = newState; + } + } + + private class TunnelReceiver implements Runnable { + public void run() { + while(stub.isConnected()){ + Address dest = null; + int len; + byte[] data = null; + DataInputStream input = null; + try{ + input = stub.getInputStream(); + dest = Util.readAddress(input); + len = input.readInt(); + if(len > 0){ + data = new byte[len]; + input.readFully(data, 0, len); + receive(dest, null/*src will be read from data*/, data, 0, len); + } + }catch(SocketException se){ + // if(log.isWarnEnabled()) log.warn("failure in TUNNEL + // receiver thread", se); + }catch(IOException ioe){ + // if(log.isWarnEnabled()) log.warn("failure in TUNNEL + // receiver thread", ioe); + }catch(Exception e){ + if(log.isWarnEnabled()) + log.warn("failure in TUNNEL receiver thread", e); + } + } + } + } + + public void sendToAllMembers(byte[] data, int offset, int length) throws Exception { + stub.sendToAllMembers(data, offset, length); + } + + public void sendToSingleMember(Address dest, byte[] data, int offset, int length) throws Exception { + stub.sendToSingleMember(dest, data, offset, length); + } + + public String getInfo() { + if(stub != null) + return stub.toString(); + else + return "RouterStub not yet initialized"; + } + + public void postUnmarshalling(Message msg, Address dest, Address src, boolean multicast) { + msg.setDest(dest); + } + + public void postUnmarshallingList(Message msg, Address dest, boolean multicast) { + msg.setDest(dest); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/TcpHeader.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/TcpHeader.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/TcpHeader.java 17 Aug 2012 14:51:06 -0000 1.1 @@ -0,0 +1,48 @@ +// $Id: TcpHeader.java,v 1.1 2012/08/17 14:51:06 marcin Exp $ + +package org.jgroups.protocols; + + +import org.jgroups.Header; +import org.jgroups.util.Streamable; + +import java.io.*; + + + + +public class TcpHeader extends Header implements Streamable { + public String group_addr=null; + + public TcpHeader() { + } // used for externalization + + public TcpHeader(String n) { + group_addr=n; + } + + public String toString() { + return "[TCP:group_addr=" + group_addr + ']'; + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeObject(group_addr); + } + + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + group_addr=(String)in.readObject(); + } + + public void writeTo(DataOutputStream out) throws IOException { + out.writeUTF(group_addr); + } + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + group_addr=in.readUTF(); + } + + public int size() { + return group_addr.length() +2; + } +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/TpHeader.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/TpHeader.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/TpHeader.java 17 Aug 2012 14:51:06 -0000 1.1 @@ -0,0 +1,57 @@ +package org.jgroups.protocols; + + +import org.jgroups.Header; +import org.jgroups.util.Streamable; + +import java.io.*; + + + +/** + * Generic transport header, used by TP. + * @author Bela Ban + * @version $Id: TpHeader.java,v 1.1 2012/08/17 14:51:06 marcin Exp $ + */ +public class TpHeader extends Header implements Streamable { + public String channel_name=null; + int size=0; + + public TpHeader() { + } // used for externalization + + public TpHeader(String n) { + channel_name=n; + if(channel_name != null) + size=channel_name.length()+2; // +2 for writeUTF() + } + + public String toString() { + return "[channel_name=" + channel_name + ']'; + } + + + public int size() { + return size; + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeUTF(channel_name); + } + + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + channel_name=in.readUTF(); + } + + + public void writeTo(DataOutputStream out) throws IOException { + out.writeUTF(channel_name); + } + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + channel_name=in.readUTF(); + if(channel_name != null) + size=channel_name.length()+2; // +2 for writeUTF() + } +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/TransportedVectorTime.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/TransportedVectorTime.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/TransportedVectorTime.java 17 Aug 2012 14:51:08 -0000 1.1 @@ -0,0 +1,166 @@ +// $Id: TransportedVectorTime.java,v 1.1 2012/08/17 14:51:08 marcin Exp $ + +package org.jgroups.protocols; + + +import org.jgroups.Message; + +import java.io.Serializable; + + + +/** + * Lighweight representation of the VectorTime clock suitable for network transport + * + * @author Vladimir Blagojevic vladimir@cs.yorku.ca + * @version $Revision: 1.1 $ + */ +public class TransportedVectorTime implements Serializable +{ + /** + * index of the sender + */ + int senderPosition; + + /** + * values represented as an array + */ + int[] values; + + /** + * Message associated with this vector clock + */ + private transient Message m; + private static final long serialVersionUID = 5857647322589533545L; + + /** + * + */ + public TransportedVectorTime() + { + } + + /** + * Constructs TransportedVectorTime with sender index and vector values + * + * @param senderIndex index of the sender of the message + * @param values vector values + */ + public TransportedVectorTime(int senderIndex, int[] values) + { + this.values = values; + this.senderPosition = senderIndex; + } + + /** + * Returns sender index + * @return sender index position + */ + public int getSenderIndex() + { + return senderPosition; + } + + /** + * Returns vector values + * @return an array of vector values + */ + public int[] getValues() + { + return values; + } + + /** + * Returns size of this vector timestamp i.e number of process group members + * @return vector timestamp size + */ + public int size() + { + return values.length; + } + + /** + * Sets a message associated with this vector timestamp + * @param owner Message that is associated with this vector timestamp + */ + public void setAssociatedMessage(Message owner) + { + m = owner; + } + + /** + * Returns a message associated with this vector timestamp. + * @return Message associated with this vector timestamp + */ + public Message getAssociatedMessage() + { + return m; + } + + + /** + *

        + *Checks if this TransportedVectorTime is less than or equal to the the specified TransportedVectorTime. + *The check is done as follows: + *

        + *

        + * VT1<=VT2 iff for every i:1..k VT1[i]<=VT2[i] + *

        + * @param other TransportedVectorTimebeing compared with this. + * @return true if this TransportedVectorTimeis less than or equal from + * other, false othwerwise + */ + public boolean lessThanOrEqual(TransportedVectorTime other) + { + int[] b = other.getValues(); + int[] a = values; + for (int k = 0; k < a.length; k++) + { + if (a[k] < b[k]) + return true; + else if (a[k] > b[k]) + return false; + } + return true; // equals ?!? + } + + /** + *

        + * Checks if this TransportedVectorTimeis equal to the specified TransportedVectorTime. + * The check is done as follows: + *

        + *

        + * VT1==VT2 iff for every i:1..k VT1[i]==VT2[i] + * @param other TransportedVectorTimebeing compared with this. + * @return true if the equation given above is true, false otherwise + */ + public boolean equals(Object other) + { + int a [] = getValues(); + int b [] = ((TransportedVectorTime)other).getValues(); + + for (int i = 0; i < a.length; i++) + if (a[i] != b[i]) return false; + + return true; + } + + /** + * Returns String representation of this vector timestamp + * @return String representing this vetor timestamp + */ + public String toString() + { + String classType = "TransportedVectorTime["; + int bufferLength = classType.length() + values.length * 2 + 1; + StringBuilder buf = new StringBuilder(bufferLength); + buf.append(classType); + for (int i = 0; i < values.length - 1; i++) + { + buf.append(values[i]).append(','); + } + buf.append(values[values.length - 1]); + buf.append(']'); + return buf.toString(); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/TunnelHeader.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/TunnelHeader.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/TunnelHeader.java 17 Aug 2012 14:51:06 -0000 1.1 @@ -0,0 +1,47 @@ +// $Id: TunnelHeader.java,v 1.1 2012/08/17 14:51:06 marcin Exp $ + +package org.jgroups.protocols; + +import org.jgroups.Header; +import org.jgroups.util.Streamable; +import org.jgroups.util.Util; + +import java.io.*; + + +public class TunnelHeader extends Header implements Streamable { + public String channel_name=null; + + public TunnelHeader() {} // used for externalization + + public TunnelHeader(String n) {channel_name=n;} + + public int size() { + return channel_name == null? 1 : channel_name.length() +3; + } + + public String toString() { + return "[TUNNEL:channel_name=" + channel_name + ']'; + } + + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeObject(channel_name); + } + + + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + channel_name=(String)in.readObject(); + } + + + public void writeTo(DataOutputStream out) throws IOException { + Util.writeString(channel_name, out); + } + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + channel_name=Util.readString(in); + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/UDP.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/UDP.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/UDP.java 17 Aug 2012 14:51:05 -0000 1.1 @@ -0,0 +1,978 @@ +package org.jgroups.protocols; + + +import org.jgroups.Address; +import org.jgroups.Global; +import org.jgroups.Message; +import org.jgroups.stack.IpAddress; +import org.jgroups.util.BoundedList; +import org.jgroups.util.Util; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Properties; + + + + +/** + * IP multicast transport based on UDP. Messages to the group (msg.dest == null) will + * be multicast (to all group members), whereas point-to-point messages + * (msg.dest != null) will be unicast to a single member. Uses a multicast and + * a unicast socket.

        + * The following properties are read by the UDP protocol: + *

          + *
        • param mcast_addr - the multicast address to use; default is 228.8.8.8. + *
        • param mcast_port - (int) the port that the multicast is sent on; default is 7600 + *
        • param ip_mcast - (boolean) flag whether to use IP multicast; default is true. + *
        • param ip_ttl - the default time-to-live for multicast packets sent out on this + * socket; default is 32. + *
        • param use_packet_handler - boolean, defaults to false. + * If set, the mcast and ucast receiver threads just put + * the datagram's payload (a byte buffer) into a queue, from where a separate thread + * will dequeue and handle them (unmarshal and pass up). This frees the receiver + * threads from having to do message unmarshalling; this time can now be spent + * receiving packets. If you have lots of retransmissions because of network + * input buffer overflow, consider setting this property to true. + *
        + * @author Bela Ban + * @version $Id: UDP.java,v 1.1 2012/08/17 14:51:05 marcin Exp $ + */ +public class UDP extends TP implements Runnable { + + /** Socket used for + *
          + *
        1. sending unicast packets and + *
        2. receiving unicast packets + *
        + * The address of this socket will be our local address (local_addr) */ + DatagramSocket sock=null; + + /** + * BoundedList of the last 100 ports used. This is to avoid reusing a port for DatagramSocket + */ + private static final BoundedList last_ports_used=new BoundedList(100); + + private static final boolean can_bind_to_mcast_addr; // are we running on Linux ? + + static { + can_bind_to_mcast_addr=Util.checkForLinux() || Util.checkForSolaris(); + } + + /** IP multicast socket for sending and receiving multicast packets */ + MulticastSocket mcast_sock=null; + + + /** If we have multiple mcast send sockets, e.g. send_interfaces or send_on_all_interfaces enabled */ + MulticastSocket[] mcast_send_sockets=null; + + /** + * Traffic class for sending unicast and multicast datagrams. + * Valid values are (check {@link DatagramSocket#setTrafficClass(int)} ); for details): + *
          + *
        • IPTOS_LOWCOST (0x02), decimal 2
        • + *
        • IPTOS_RELIABILITY (0x04)<, decimal 4/LI> + *
        • IPTOS_THROUGHPUT (0x08), decimal 8
        • + *
        • IPTOS_LOWDELAY (0x10), decimal 16
        • + *
        + */ + int tos=8; // valid values: 2, 4, 8 (default), 16 + + + /** The multicast address (mcast address and port) this member uses */ + IpAddress mcast_addr=null; + + /** The multicast address used for sending and receiving packets */ + String mcast_addr_name="228.8.8.8"; + + /** The multicast port used for sending and receiving packets */ + int mcast_port=7600; + + /** The multicast receiver thread */ + Thread mcast_receiver=null; + + private final static String MCAST_RECEIVER_THREAD_NAME = "UDP mcast"; + + /** The unicast receiver thread */ + UcastReceiver ucast_receiver=null; + + /** Whether to enable IP multicasting. If false, multiple unicast datagram + * packets are sent rather than one multicast packet */ + boolean ip_mcast=true; + + /** The time-to-live (TTL) for multicast datagram packets */ + int ip_ttl=8; + + /** Send buffer size of the multicast datagram socket */ + int mcast_send_buf_size=32000; + + /** Receive buffer size of the multicast datagram socket */ + int mcast_recv_buf_size=64000; + + /** Send buffer size of the unicast datagram socket */ + int ucast_send_buf_size=32000; + + /** Receive buffer size of the unicast datagram socket */ + int ucast_recv_buf_size=64000; + + + /** Usually, src addresses are nulled, and the receiver simply sets them to the address of the sender. However, + * for multiple addresses on a Windows loopback device, this doesn't work + * (see http://jira.jboss.com/jira/browse/JGRP-79 and the JGroups wiki for details). This must be the same + * value for all members of the same group. Default is true, for performance reasons */ + // private boolean null_src_addresses=true; + + + + /** + * Creates the UDP protocol, and initializes the + * state variables, does however not start any sockets or threads. + */ + public UDP() { + } + + + + /** + * Setup the Protocol instance acording to the configuration string. + * The following properties are read by the UDP protocol: + *
          + *
        • param mcast_addr - the multicast address to use default is 228.8.8.8 + *
        • param mcast_port - (int) the port that the multicast is sent on default is 7600 + *
        • param ip_mcast - (boolean) flag whether to use IP multicast - default is true + *
        • param ip_ttl - Set the default time-to-live for multicast packets sent out on this socket. default is 32 + *
        + * @return true if no other properties are left. + * false if the properties still have data in them, ie , + * properties are left over and not handled by the protocol stack + */ + public boolean setProperties(Properties props) { + String str; + + super.setProperties(props); + + str=props.getProperty("num_last_ports"); + if(str != null) { + props.remove("num_last_ports"); + log.warn("num_last_ports has been deprecated, property will be ignored"); + } + + str=Util.getProperty(new String[]{Global.UDP_MCAST_ADDR, "jboss.partition.udpGroup"}, props, + "mcast_addr", false, "228.8.8.8"); + if(str != null) + mcast_addr_name=str; + + str=Util.getProperty(new String[]{Global.UDP_MCAST_PORT, "jboss.partition.udpPort"}, + props, "mcast_port", false, "7600"); + if(str != null) + mcast_port=Integer.parseInt(str); + + str=props.getProperty("ip_mcast"); + if(str != null) { + ip_mcast=Boolean.valueOf(str).booleanValue(); + props.remove("ip_mcast"); + } + + str=Util.getProperty(new String[]{Global.UDP_IP_TTL}, props, "ip_ttl", false, "64"); + if(str != null) { + ip_ttl=Integer.parseInt(str); + props.remove("ip_ttl"); + } + + str=props.getProperty("tos"); + if(str != null) { + tos=Integer.parseInt(str); + props.remove("tos"); + } + + str=props.getProperty("mcast_send_buf_size"); + if(str != null) { + mcast_send_buf_size=Integer.parseInt(str); + props.remove("mcast_send_buf_size"); + } + + str=props.getProperty("mcast_recv_buf_size"); + if(str != null) { + mcast_recv_buf_size=Integer.parseInt(str); + props.remove("mcast_recv_buf_size"); + } + + str=props.getProperty("ucast_send_buf_size"); + if(str != null) { + ucast_send_buf_size=Integer.parseInt(str); + props.remove("ucast_send_buf_size"); + } + + str=props.getProperty("ucast_recv_buf_size"); + if(str != null) { + ucast_recv_buf_size=Integer.parseInt(str); + props.remove("ucast_recv_buf_size"); + } + + str=props.getProperty("null_src_addresses"); + if(str != null) { + // null_src_addresses=Boolean.valueOf(str).booleanValue(); + props.remove("null_src_addresses"); + log.error("null_src_addresses has been deprecated, property will be ignored"); + } + + //https://jira.jboss.org/jira/browse/JGRP-865 + //https://jira.jboss.org/jira/browse/JGRP-606 + str=props.getProperty("end_port"); + if(str != null) { + port_range=Integer.parseInt(str) - bind_port; + props.remove("end_port"); + } + + Util.checkBufferSize("UDP.mcast_send_buf_size", mcast_send_buf_size); + Util.checkBufferSize("UDP.mcast_recv_buf_size", mcast_recv_buf_size); + Util.checkBufferSize("UDP.ucast_send_buf_size", ucast_send_buf_size); + Util.checkBufferSize("UDP.ucast_recv_buf_size", ucast_recv_buf_size); + return props.isEmpty(); + } + + + + public void setMcastPort(int p) { + mcast_port=p; + } + + + /* ----------------------- Receiving of MCAST UDP packets ------------------------ */ + + public void run() { + DatagramPacket packet; + byte receive_buf[]=new byte[65535]; + int offset, len, sender_port; + byte[] data; + InetAddress sender_addr; + Address sender; + + + // moved out of loop to avoid excessive object creations (bela March 8 2001) + packet=new DatagramPacket(receive_buf, receive_buf.length); + + while(mcast_receiver != null && mcast_sock != null) { + try { + packet.setData(receive_buf, 0, receive_buf.length); + mcast_sock.receive(packet); + sender_addr=packet.getAddress(); + sender_port=packet.getPort(); + offset=packet.getOffset(); + len=packet.getLength(); + data=packet.getData(); + sender=new IpAddress(sender_addr, sender_port); + + if(len > receive_buf.length) { + if(log.isErrorEnabled()) + log.error("size of the received packet (" + len + ") is bigger than " + + "allocated buffer (" + receive_buf.length + "): will not be able to handle packet. " + + "Use the FRAG protocol and make its frag_size lower than " + receive_buf.length); + } + + receive(mcast_addr, sender, data, offset, len); + } + catch(SocketException sock_ex) { + if(log.isTraceEnabled()) log.trace("multicast socket is closed, exception=" + sock_ex); + break; + } + catch(InterruptedIOException io_ex) { // thread was interrupted + } + catch(Throwable ex) { + if(log.isErrorEnabled()) + log.error("failure in multicast receive()", ex); + // Util.sleep(100); // so we don't get into 100% cpu spinning (should NEVER happen !) + } + } + if(log.isDebugEnabled()) log.debug("multicast thread terminated"); + } + + public String getInfo() { + StringBuilder sb=new StringBuilder(); + sb.append("group_addr=").append(mcast_addr_name).append(':').append(mcast_port).append("\n"); + return sb.toString(); + } + + public void sendToAllMembers(byte[] data, int offset, int length) throws Exception { + if(ip_mcast && mcast_addr != null) { + _send(mcast_addr.getIpAddress(), mcast_addr.getPort(), true, data, offset, length); + } + else { + List
        mbrs; + synchronized(members) { + mbrs=new ArrayList
        (members); + } + for(Address mbr: mbrs) { + _send(((IpAddress)mbr).getIpAddress(), ((IpAddress)mbr).getPort(), false, data, offset, length); + } + } + } + + public void sendToSingleMember(Address dest, byte[] data, int offset, int length) throws Exception { + _send(((IpAddress)dest).getIpAddress(), ((IpAddress)dest).getPort(), false, data, offset, length); + } + + + public void postUnmarshalling(Message msg, Address dest, Address src, boolean multicast) { + if(multicast) + msg.setDest(null); + else + msg.setDest(dest); + } + + public void postUnmarshallingList(Message msg, Address dest, boolean multicast) { + if(multicast) + msg.setDest(null); + else + msg.setDest(dest); + } + + private void _send(InetAddress dest, int port, boolean mcast, byte[] data, int offset, int length) throws Exception { + DatagramPacket packet=new DatagramPacket(data, offset, length, dest, port); + try { + if(mcast) { + if(mcast_send_sockets != null) { + MulticastSocket s; + for(int i=0; i < mcast_send_sockets.length; i++) { + s=mcast_send_sockets[i]; + try { + s.send(packet); + } + catch(Exception e) { + log.error("failed sending packet on socket " + s); + } + } + } + else { // DEFAULT path + if(mcast_sock != null) + mcast_sock.send(packet); + } + } + else { + if(sock != null) + sock.send(packet); + } + } + catch(Exception ex) { + throw new Exception("dest=" + dest + ":" + port + " (" + length + " bytes)", ex); + } + } + + + /* ------------------------------------------------------------------------------- */ + + + + /*------------------------------ Protocol interface ------------------------------ */ + + public String getName() { + return "UDP"; + } + + + + + /** + * Creates the unicast and multicast sockets and starts the unicast and multicast receiver threads + */ + public void start() throws Exception { + if(log.isDebugEnabled()) log.debug("creating sockets and starting threads"); + try { + createSockets(); + } + catch(Exception ex) { + String tmp="problem creating sockets (bind_addr=" + bind_addr + ", mcast_addr=" + mcast_addr + ")"; + throw new Exception(tmp, ex); + } + super.start(); + // startThreads(); // moved to handleConnect() + } + + + public void stop() { + if(log.isDebugEnabled()) log.debug("closing sockets and stopping threads"); + stopThreads(); // will close sockets, closeSockets() is not really needed anymore, but... + closeSockets(); // ... we'll leave it in there for now (doesn't do anything if already closed) + super.stop(); + } + + + protected void handleConnect() throws Exception { + if(singleton_name != null && singleton_name.length() > 0) { + if(connect_count == 0) { + startThreads(); + } + super.handleConnect(); + } + else + startThreads(); + } + + protected void handleDisconnect() { + if(singleton_name != null && singleton_name.length() > 0) { + super.handleDisconnect(); + if(connect_count == 0) { + stopThreads(); + } + } + else + stopThreads(); + } + + /*--------------------------- End of Protocol interface -------------------------- */ + + + /* ------------------------------ Private Methods -------------------------------- */ + + + + + + /** + * Create UDP sender and receiver sockets. Currently there are 2 sockets + * (sending and receiving). This is due to Linux's non-BSD compatibility + * in the JDK port (see DESIGN). + */ + private void createSockets() throws Exception { + InetAddress tmp_addr; + + // bind_addr not set, try to assign one by default. This is needed on Windows + + // changed by bela Feb 12 2003: by default multicast sockets will be bound to all network interfaces + + // CHANGED *BACK* by bela March 13 2003: binding to all interfaces did not result in a correct + // local_addr. As a matter of fact, comparison between e.g. 0.0.0.0:1234 (on hostA) and + // 0.0.0.0:1.2.3.4 (on hostB) would fail ! +// if(bind_addr == null) { +// InetAddress[] interfaces=InetAddress.getAllByName(InetAddress.getLocalHost().getHostAddress()); +// if(interfaces != null && interfaces.length > 0) +// bind_addr=interfaces[0]; +// } + + if(bind_addr == null && !use_local_host) { + bind_addr=Util.getFirstNonLoopbackAddress(); + } + if(bind_addr == null) + bind_addr=InetAddress.getLocalHost(); + + if(bind_addr != null) + if(log.isDebugEnabled()) log.debug("sockets will use interface " + bind_addr.getHostAddress()); + + + // 2. Create socket for receiving unicast UDP packets. The address and port + // of this socket will be our local address (local_addr) + if(bind_port > 0) { + sock=createDatagramSocketWithBindPort(); + } + else { + sock=createEphemeralDatagramSocket(); + } + if(tos > 0) { + try { + sock.setTrafficClass(tos); + } + catch(SocketException e) { + log.warn("traffic class of " + tos + " could not be set, will be ignored"); + if(log.isDebugEnabled()) + log.debug("Cause of failure to set traffic class:", e); + } + } + + if(sock == null) + throw new Exception("UDP.createSocket(): sock is null"); + + local_addr=createLocalAddress(); + if(additional_data != null) + ((IpAddress)local_addr).setAdditionalData(additional_data); + + + // 3. Create socket for receiving IP multicast packets + if(ip_mcast) { + // 3a. Create mcast receiver socket + tmp_addr=InetAddress.getByName(mcast_addr_name); + + // https://jira.jboss.org/jira/browse/JGRP-777 - this doesn't work on MacOS, and we don't have + // cross talking on Windows anyway, so we just do it for Linux. (How about Solaris ?) + if(can_bind_to_mcast_addr) + mcast_sock=Util.createMulticastSocket(tmp_addr, mcast_port, log); + else + mcast_sock=new MulticastSocket(mcast_port); + + mcast_sock.setTimeToLive(ip_ttl); + + mcast_addr=new IpAddress(tmp_addr, mcast_port); + if(tos > 0) { + try { + mcast_sock.setTrafficClass(tos); + } + catch(SocketException e) { + log.warn("traffic class of " + tos + " could not be set, will be ignored", e); + } + } + + if(receive_on_all_interfaces || (receive_interfaces != null && !receive_interfaces.isEmpty())) { + List interfaces; + if(receive_interfaces != null) + interfaces=receive_interfaces; + else + interfaces=Util.getAllAvailableInterfaces(); + bindToInterfaces(interfaces, mcast_sock, mcast_addr.getIpAddress()); + } + else { + if(bind_addr != null) + mcast_sock.setInterface(bind_addr); + mcast_sock.joinGroup(tmp_addr); + } + + // 3b. Create mcast sender socket + if(send_on_all_interfaces || (send_interfaces != null && !send_interfaces.isEmpty())) { + List interfaces; + if(send_interfaces != null) + interfaces=send_interfaces; + else + interfaces=Util.getAllAvailableInterfaces(); + mcast_send_sockets=new MulticastSocket[interfaces.size()]; + int index=0; + for(NetworkInterface intf: interfaces) { + mcast_send_sockets[index]=new MulticastSocket(); + mcast_send_sockets[index].setNetworkInterface(intf); + mcast_send_sockets[index].setTimeToLive(ip_ttl); + if(tos > 0) { + try { + mcast_send_sockets[index].setTrafficClass(tos); + } + catch(SocketException e) { + log.warn("traffic class of " + tos + " could not be set, will be ignored", e); + } + } + index++; + } + } + } + + setBufferSizes(); + if(log.isDebugEnabled()) log.debug("socket information:\n" + dumpSocketInfo()); + } + + + protected Address createLocalAddress() { + return new IpAddress(sock.getLocalAddress(), sock.getLocalPort()); + } + + + + /** + * + * @param interfaces List. Guaranteed to have no duplicates + * @param s + * @param mcastAddr + * @throws IOException + */ + private void bindToInterfaces(List interfaces, MulticastSocket s, InetAddress mcastAddr) throws IOException { + SocketAddress tmp_mcast_addr=new InetSocketAddress(mcastAddr, mcast_port); + for(NetworkInterface intf: interfaces) { + s.joinGroup(tmp_mcast_addr, intf); + if(log.isTraceEnabled()) + log.trace("joined " + tmp_mcast_addr + " on " + intf.getName()); + } + } + + + + /** Creates a DatagramSocket with a random port. Because in certain operating systems, ports are reused, + * we keep a list of the n last used ports, and avoid port reuse */ + protected DatagramSocket createEphemeralDatagramSocket() throws SocketException { + DatagramSocket tmp; + int localPort=0; + while(true) { + try { + tmp=new DatagramSocket(localPort, bind_addr); // first time localPort is 0 + } + catch(SocketException socket_ex) { + // Vladimir May 30th 2007 + // special handling for Linux 2.6 kernel which sometimes throws BindException while we probe for a random port + localPort++; + continue; + } + localPort=tmp.getLocalPort(); + if(last_ports_used.contains(localPort)) { + if(log.isDebugEnabled()) + log.debug("local port " + localPort + " already seen in this session; will try to get other port"); + try {tmp.close();} catch(Throwable e) {} + localPort++; + } + else { + last_ports_used.add(localPort); + break; + } + } + return tmp; + } + + + + + /** + * Creates a DatagramSocket when bind_port > 0. Attempts to allocate the socket with port == bind_port, and + * increments until it finds a valid port, or until port_range has been exceeded + * @return DatagramSocket The newly created socket + * @throws Exception + */ + protected DatagramSocket createDatagramSocketWithBindPort() throws Exception { + DatagramSocket tmp=null; + // 27-6-2003 bgooren, find available port in range (start_port, start_port+port_range) + int rcv_port=bind_port, max_port=bind_port + port_range; + if(pm != null && bind_port > 0) { + rcv_port=pm.getNextAvailablePort(rcv_port); + } + while(rcv_port <= max_port) { + try { + tmp=new DatagramSocket(rcv_port, bind_addr); + return tmp; + } + catch(SocketException bind_ex) { // Cannot listen on this port + rcv_port++; + } + catch(SecurityException sec_ex) { // Not allowed to listen on this port + rcv_port++; + } + } + + // Cannot listen at all, throw an Exception + if(rcv_port >= max_port + 1) { // +1 due to the increment above + throw new Exception("failed to open a port in range " + bind_port + '-' + max_port); + } + return tmp; + } + + + private String dumpSocketInfo() throws Exception { + StringBuilder sb=new StringBuilder(128); + sb.append("local_addr=").append(local_addr); + sb.append(", mcast_addr=").append(mcast_addr); + sb.append(", bind_addr=").append(bind_addr); + sb.append(", ttl=").append(ip_ttl); + + if(sock != null) { + sb.append("\nsock: bound to "); + sb.append(sock.getLocalAddress().getHostAddress()).append(':').append(sock.getLocalPort()); + sb.append(", receive buffer size=").append(sock.getReceiveBufferSize()); + sb.append(", send buffer size=").append(sock.getSendBufferSize()); + } + + if(mcast_sock != null) { + sb.append("\nmcast_sock: bound to "); + sb.append(mcast_sock.getInterface().getHostAddress()).append(':').append(mcast_sock.getLocalPort()); + sb.append(", send buffer size=").append(mcast_sock.getSendBufferSize()); + sb.append(", receive buffer size=").append(mcast_sock.getReceiveBufferSize()); + } + + + if(mcast_send_sockets != null) { + sb.append("\n").append(mcast_send_sockets.length).append(" mcast send sockets:\n"); + MulticastSocket s; + for(int i=0; i < mcast_send_sockets.length; i++) { + s=mcast_send_sockets[i]; + sb.append(s.getInterface().getHostAddress()).append(':').append(s.getLocalPort()); + sb.append(", send buffer size=").append(s.getSendBufferSize()); + sb.append(", receive buffer size=").append(s.getReceiveBufferSize()).append("\n"); + } + } + return sb.toString(); + } + + + void setBufferSizes() { + if(sock != null) + setBufferSize(sock, ucast_send_buf_size, ucast_recv_buf_size); + + if(mcast_sock != null) + setBufferSize(mcast_sock, mcast_send_buf_size, mcast_recv_buf_size); + + if(mcast_send_sockets != null) { + for(int i=0; i < mcast_send_sockets.length; i++) { + setBufferSize(mcast_send_sockets[i], mcast_send_buf_size, mcast_recv_buf_size); + } + } + } + + private void setBufferSize(DatagramSocket sock, int send_buf_size, int recv_buf_size) { + try { + sock.setSendBufferSize(send_buf_size); + } + catch(Throwable ex) { + if(log.isWarnEnabled()) log.warn("failed setting send buffer size of " + send_buf_size + " in " + sock + ": " + ex); + } + + try { + sock.setReceiveBufferSize(recv_buf_size); + } + catch(Throwable ex) { + if(log.isWarnEnabled()) log.warn("failed setting receive buffer size of " + recv_buf_size + " in " + sock + ": " + ex); + } + } + + + /** + * Closed UDP unicast and multicast sockets + */ + void closeSockets() { + // 1. Close multicast socket + closeMulticastSocket(); + + // 2. Close socket + closeSocket(); + } + + + void closeMulticastSocket() { + if(mcast_sock != null) { + try { + if(mcast_addr != null) { + mcast_sock.leaveGroup(mcast_addr.getIpAddress()); + } + mcast_sock.close(); // this will cause the mcast receiver thread to break out of its loop + mcast_sock=null; + if(log.isDebugEnabled()) log.debug("multicast socket closed"); + } + catch(IOException ex) { + } + mcast_addr=null; + } + + if(mcast_send_sockets != null) { + MulticastSocket s; + for(int i=0; i < mcast_send_sockets.length; i++) { + s=mcast_send_sockets[i]; + s.close(); + if(log.isDebugEnabled()) log.debug("multicast send socket " + s + " closed"); + } + mcast_send_sockets=null; + } + } + + + private void closeSocket() { + if(sock != null) { + if(pm != null && bind_port > 0) { + int port=local_addr != null? ((IpAddress)local_addr).getPort() : sock.getLocalPort(); + pm.updatePort(port); + } + sock.close(); + sock=null; + if(log.isDebugEnabled()) log.debug("socket closed"); + } + } + + + + + /** + * Starts the unicast and multicast receiver threads + */ + void startThreads() throws Exception { + if(ucast_receiver == null) { + //start the listener thread of the ucast_recv_sock + ucast_receiver=new UcastReceiver(); + ucast_receiver.start(); + + + global_thread_factory.renameThread(UcastReceiver.UCAST_RECEIVER_THREAD_NAME, ucast_receiver.getThread()); + + if(log.isDebugEnabled()) + log.debug("created unicast receiver thread " + ucast_receiver.getThread()); + } + + if(ip_mcast) { + if(mcast_receiver != null) { + if(mcast_receiver.isAlive()) { + if(log.isDebugEnabled()) log.debug("did not create new multicastreceiver thread as existing " + + "multicast receiver thread is still running"); + } + else + mcast_receiver=null; // will be created just below... + } + + if(mcast_receiver == null) { + mcast_receiver=global_thread_factory.newThread(this,MCAST_RECEIVER_THREAD_NAME); + mcast_receiver.setPriority(Thread.MAX_PRIORITY); // needed ???? + mcast_receiver.start(); + if(log.isDebugEnabled()) + log.debug("created multicast receiver thread " + mcast_receiver); + } + } + } + + + /** + * Stops unicast and multicast receiver threads + */ + void stopThreads() { + Thread tmp; + + // 1. Stop the multicast receiver thread + if(mcast_receiver != null) { + if(mcast_receiver.isAlive()) { + tmp=mcast_receiver; + mcast_receiver=null; + closeMulticastSocket(); // will cause the multicast thread to terminate + tmp.interrupt(); + try { + tmp.join(Global.THREAD_SHUTDOWN_WAIT_TIME); + } + catch(InterruptedException e) { + Thread.currentThread().interrupt(); // set interrupt flag again + } + tmp=null; + } + mcast_receiver=null; + } + + // 2. Stop the unicast receiver thread + if(ucast_receiver != null) { + ucast_receiver.stop(); + ucast_receiver=null; + } + } + + + protected void setThreadNames() { + super.setThreadNames(); + + global_thread_factory.renameThread(MCAST_RECEIVER_THREAD_NAME, mcast_receiver); + if(ucast_receiver != null) + global_thread_factory.renameThread(UcastReceiver.UCAST_RECEIVER_THREAD_NAME, + ucast_receiver.getThread()); + } + + protected void unsetThreadNames() { + super.unsetThreadNames(); + if(mcast_receiver != null) + mcast_receiver.setName(MCAST_RECEIVER_THREAD_NAME); + + if(ucast_receiver != null && ucast_receiver.getThread() != null) + ucast_receiver.getThread().setName(UcastReceiver.UCAST_RECEIVER_THREAD_NAME); + } + + + protected void handleConfigEvent(Map map) { + boolean set_buffers=false; + super.handleConfigEvent(map); + if(map == null) return; + + if(map.containsKey("send_buf_size")) { + mcast_send_buf_size=((Integer)map.get("send_buf_size")).intValue(); + ucast_send_buf_size=mcast_send_buf_size; + set_buffers=true; + } + if(map.containsKey("recv_buf_size")) { + mcast_recv_buf_size=((Integer)map.get("recv_buf_size")).intValue(); + ucast_recv_buf_size=mcast_recv_buf_size; + set_buffers=true; + } + if(set_buffers) + setBufferSizes(); + } + + + + /* ----------------------------- End of Private Methods ---------------------------------------- */ + + /* ----------------------------- Inner Classes ---------------------------------------- */ + + + + + public class UcastReceiver implements Runnable { + + public static final String UCAST_RECEIVER_THREAD_NAME = "UDP ucast"; + boolean running=true; + Thread thread=null; + + public Thread getThread(){ + return thread; + } + + + public void start() { + if(thread == null) { + thread=global_thread_factory.newThread(this,UCAST_RECEIVER_THREAD_NAME); + // thread.setDaemon(true); + running=true; + thread.start(); + } + } + + + public void stop() { + Thread tmp; + if(thread != null && thread.isAlive()) { + running=false; + tmp=thread; + thread=null; + closeSocket(); // this will cause the thread to break out of its loop + tmp.interrupt(); + try { + tmp.join(Global.THREAD_SHUTDOWN_WAIT_TIME); + } + catch(InterruptedException e) { + Thread.currentThread().interrupt(); // set interrupt flag again + } + tmp=null; + } + thread=null; + } + + + public void run() { + DatagramPacket packet; + byte receive_buf[]=new byte[65535]; + int offset, len; + byte[] data; + InetAddress sender_addr; + int sender_port; + Address sender; + + // moved out of loop to avoid excessive object creations (bela March 8 2001) + packet=new DatagramPacket(receive_buf, receive_buf.length); + + while(running && thread != null && sock != null) { + try { + packet.setData(receive_buf, 0, receive_buf.length); + sock.receive(packet); + sender_addr=packet.getAddress(); + sender_port=packet.getPort(); + offset=packet.getOffset(); + len=packet.getLength(); + data=packet.getData(); + sender=new IpAddress(sender_addr, sender_port); + + if(len > receive_buf.length) { + if(log.isErrorEnabled()) + log.error("size of the received packet (" + len + ") is bigger than allocated buffer (" + + receive_buf.length + "): will not be able to handle packet. " + + "Use the FRAG protocol and make its frag_size lower than " + receive_buf.length); + } + receive(local_addr, sender, data, offset, len); + } + catch(SocketException sock_ex) { + if(log.isDebugEnabled()) log.debug("unicast receiver socket is closed, exception=" + sock_ex); + break; + } + catch(InterruptedIOException io_ex) { // thread was interrupted + } + catch(Throwable ex) { + if(log.isErrorEnabled()) + log.error("[" + local_addr + "] failed receiving unicast packet", ex); + // Util.sleep(100); // so we don't get into 100% cpu spinning (should NEVER happen !) + } + } + if(log.isDebugEnabled()) log.debug("unicast receiver thread terminated"); + } + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/UNICAST.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/UNICAST.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/UNICAST.java 17 Aug 2012 14:51:08 -0000 1.1 @@ -0,0 +1,758 @@ + +package org.jgroups.protocols; + +import org.jgroups.*; +import org.jgroups.stack.AckReceiverWindow; +import org.jgroups.stack.AckSenderWindow; +import org.jgroups.stack.Protocol; +import org.jgroups.stack.StaticInterval; +import org.jgroups.util.BoundedList; +import org.jgroups.util.Streamable; +import org.jgroups.util.TimeScheduler; +import org.jgroups.util.Util; + +import java.io.*; +import java.util.*; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.atomic.AtomicInteger; + + +/** + * Reliable unicast layer. Uses acknowledgement scheme similar to TCP to provide lossless transmission + * of unicast messages (for reliable multicast see NAKACK layer). When a message is sent to a peer for + * the first time, we add the pair to the hashtable (peer address is the key). All + * messages sent to that peer will be added to hashtable.peer_addr.sent_msgs. When we receive a + * message from a peer for the first time, another entry will be created and added to the hashtable + * (unless already existing). Msgs will then be added to hashtable.peer_addr.received_msgs.

        This + * layer is used to reliably transmit point-to-point messages, that is, either messages sent to a + * single receiver (vs. messages multicast to a group) or for example replies to a multicast message. The + * sender uses an AckSenderWindow which retransmits messages for which it hasn't received + * an ACK, the receiver uses AckReceiverWindow which keeps track of the lowest seqno + * received so far, and keeps messages in order.

        + * Messages in both AckSenderWindows and AckReceiverWindows will be removed. A message will be removed from + * AckSenderWindow when an ACK has been received for it and messages will be removed from AckReceiverWindow + * whenever a message is received: the new message is added and then we try to remove as many messages as + * possible (until we stop at a gap, or there are no more messages). + * @author Bela Ban + * @version $Id: UNICAST.java,v 1.1 2012/08/17 14:51:08 marcin Exp $ + */ +public class UNICAST extends Protocol implements AckSenderWindow.RetransmitCommand { + private final Vector

        members=new Vector
        (11); + private final HashMap connections=new HashMap(11); + private long[] timeouts={400,800,1600,3200}; // for AckSenderWindow: max time to wait for missing acks + private Address local_addr=null; + private TimeScheduler timer=null; // used for retransmissions (passed to AckSenderWindow) + private Map locks; + + // if UNICAST is used without GMS, don't consult the membership on retransmit() if use_gms=false + // default is true + private boolean use_gms=true; + private boolean started=false; + + // ack a message before it is processed by the application to limit unnecessary retransmits + private boolean immediate_ack=false; + + /** whether to loop back messages sent to self (will be removed in the future, default=false) */ + private boolean loopback=false; + + /** + * By default, we release the lock on the sender in up() after the up() method call passed up the stack returns. + * However, with eager_lock_release enabled (default), we release the lock as soon as the application calls + * Channel.down() within the receive() callback. This leads to issues as the one described in + * http://jira.jboss.com/jira/browse/JGRP-656. Note that ordering is still correct , but messages from self + * might get delivered concurrently. This can be turned off by setting eager_lock_release to false. + */ + private boolean eager_lock_release=true; + + /** A list of members who left, used to determine when to prevent sending messages to left mbrs */ + private final BoundedList
        previous_members=new BoundedList
        (50); + /** Contains all members that were enabled for unicasts by Event.ENABLE_UNICAST_TO */ + private final BoundedList
        enabled_members=new BoundedList
        (100); + + private final static String name="UNICAST"; + private static final long DEFAULT_FIRST_SEQNO=1; + + private long num_msgs_sent=0, num_msgs_received=0, num_bytes_sent=0, num_bytes_received=0; + private long num_acks_sent=0, num_acks_received=0, num_xmit_requests_received=0; + + + /** Regular messages which have been added, but not removed */ + private final AtomicInteger undelivered_msgs=new AtomicInteger(0); + + + /** All protocol names have to be unique ! */ + public String getName() {return name;} + + public String getLocalAddress() {return local_addr != null? local_addr.toString() : "null";} + public String getMembers() {return members != null? members.toString() : "[]";} + public String printConnections() { + StringBuilder sb=new StringBuilder(); + for(Map.Entry entry: connections.entrySet()) { + sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); + } + return sb.toString(); + } + + + public long getNumMessagesSent() { + return num_msgs_sent; + } + + public long getNumMessagesReceived() { + return num_msgs_received; + } + + public long getNumBytesSent() { + return num_bytes_sent; + } + + public long getNumBytesReceived() { + return num_bytes_received; + } + + public long getNumAcksSent() { + return num_acks_sent; + } + + public long getNumAcksReceived() { + return num_acks_received; + } + + public long getNumberOfRetransmitRequestsReceived() { + return num_xmit_requests_received; + } + + /** The number of messages in all Entry.sent_msgs tables (haven't received an ACK yet) */ + public int getNumberOfUnackedMessages() { + int num=0; + synchronized(connections) { + for(Entry entry: connections.values()) { + if(entry.sent_msgs != null) + num+=entry.sent_msgs.size(); + } + } + return num; + } + + + public String getUnackedMessages() { + StringBuilder sb=new StringBuilder(); + Entry e; + Object member; + synchronized(connections) { + for(Map.Entry entry: connections.entrySet()) { + member=entry.getKey(); + e=entry.getValue(); + sb.append(member).append(": "); + if(e.sent_msgs != null) + sb.append(e.sent_msgs.toString()).append("\n"); + } + } + return sb.toString(); + } + + + public int getNumberOfMessagesInReceiveWindows() { + int num=0; + synchronized(connections) { + for(Entry entry: connections.values()) { + if(entry.received_msgs != null) + num+=entry.received_msgs.size(); + } + } + return num; + } + + public void resetStats() { + num_msgs_sent=num_msgs_received=num_bytes_sent=num_bytes_received=num_acks_sent=num_acks_received=0; + num_xmit_requests_received=0; + } + + + public Map dumpStats() { + Map m=new HashMap(); + m.put("num_msgs_sent", new Long(num_msgs_sent)); + m.put("num_msgs_received", new Long(num_msgs_received)); + m.put("num_bytes_sent", new Long(num_bytes_sent)); + m.put("num_bytes_received", new Long(num_bytes_received)); + m.put("num_acks_sent", new Long(num_acks_sent)); + m.put("num_acks_received", new Long(num_acks_received)); + m.put("num_xmit_requests_received", new Long(num_xmit_requests_received)); + m.put("num_unacked_msgs", new Long(getNumberOfUnackedMessages())); + m.put("unacked_msgs", getUnackedMessages()); + m.put("num_msgs_in_recv_windows", new Long(getNumberOfMessagesInReceiveWindows())); + return m; + } + + public boolean setProperties(Properties props) { + String str; + long[] tmp; + + super.setProperties(props); + str=props.getProperty("timeout"); + if(str != null) { + tmp=Util.parseCommaDelimitedLongs(str); + if(tmp != null && tmp.length > 0) + timeouts=tmp; + props.remove("timeout"); + } + + str=props.getProperty("window_size"); + if(str != null) { + props.remove("window_size"); + log.warn("window_size is deprecated and will be ignored"); + } + + str=props.getProperty("min_threshold"); + if(str != null) { + props.remove("min_threshold"); + log.warn("min_threshold is deprecated and will be ignored"); + } + + str=props.getProperty("use_gms"); + if(str != null) { + use_gms=Boolean.valueOf(str).booleanValue(); + props.remove("use_gms"); + } + + str=props.getProperty("immediate_ack"); + if(str != null) { + immediate_ack=Boolean.valueOf(str).booleanValue(); + props.remove("immediate_ack"); + } + + str=props.getProperty("loopback"); + if(str != null) { + loopback=Boolean.valueOf(str).booleanValue(); + props.remove("loopback"); + } + + str=props.getProperty("eager_lock_release"); + if(str != null) { + eager_lock_release=Boolean.valueOf(str).booleanValue(); + props.remove("eager_lock_release"); + } + + if(!props.isEmpty()) { + log.error("these properties are not recognized: " + props); + return false; + } + return true; + } + + public void start() throws Exception { + timer=getTransport().getTimer(); + if(timer == null) + throw new Exception("timer is null"); + locks=stack.getLocks(); + started=true; + } + + public void stop() { + started=false; + removeAllConnections(); + undelivered_msgs.set(0); + } + + + public Object up(Event evt) { + Message msg; + Address dst, src; + UnicastHeader hdr; + + switch(evt.getType()) { + + case Event.MSG: + msg=(Message)evt.getArg(); + dst=msg.getDest(); + + if(dst == null || dst.isMulticastAddress()) // only handle unicast messages + break; // pass up + + // changed from removeHeader(): we cannot remove the header because if we do loopback=true at the + // transport level, we will not have the header on retransmit ! (bela Aug 22 2006) + hdr=(UnicastHeader)msg.getHeader(name); + if(hdr == null) + break; + src=msg.getSrc(); + switch(hdr.type) { + case UnicastHeader.DATA: // received regular message + // only send an ACK if added to the received_msgs table (bela Aug 2006) + // if in immediate_ack mode, send ack inside handleDataReceived + if(handleDataReceived(src, hdr.seqno, msg) && !immediate_ack) + sendAck(src, hdr.seqno); + return null; // we pass the deliverable message up in handleDataReceived() + case UnicastHeader.ACK: // received ACK for previously sent message + handleAckReceived(src, hdr.seqno); + break; + default: + log.error("UnicastHeader type " + hdr.type + " not known !"); + break; + } + return null; + + case Event.SET_LOCAL_ADDRESS: + local_addr=(Address)evt.getArg(); + break; + } + + return up_prot.up(evt); // Pass up to the layer above us + } + + + + public Object down(Event evt) { + switch (evt.getType()) { + + case Event.MSG: // Add UnicastHeader, add to AckSenderWindow and pass down + Message msg=(Message)evt.getArg(); + Address dst=msg.getDest(); + + /* only handle unicast messages */ + if (dst == null || dst.isMulticastAddress()) { + break; + } + + if(!started) { + if(log.isTraceEnabled()) + log.trace("discarded message as start() has not yet been called, message: " + msg); + return null; + } + + // if the dest is self --> pass the message back up + if(loopback && local_addr != null && local_addr.equals(dst)) { + msg.setSrc(local_addr); + up_prot.up(evt); + num_msgs_sent++; + num_bytes_sent+=msg.getLength(); + return null; + } + + if(!members.contains(dst) && !enabled_members.contains(dst)) { + throw new IllegalArgumentException(dst + " is not a member of the group " + members + " (enabled_members=" + enabled_members + ")"); + } + + Entry entry; + synchronized(connections) { + entry=connections.get(dst); + if(entry == null) { + entry=new Entry(); + connections.put(dst, entry); + if(log.isTraceEnabled()) + log.trace(local_addr + ": created new connection for dst " + dst); + } + } + + long seqno=-2; + synchronized(entry) { // threads will only sync if they access the same entry + try { + seqno=entry.sent_msgs_seqno; + UnicastHeader hdr=new UnicastHeader(UnicastHeader.DATA, seqno); + if(entry.sent_msgs == null) { // first msg to peer 'dst' + entry.sent_msgs=new AckSenderWindow(this, new StaticInterval(timeouts), timer, this.local_addr); // use the global timer + } + msg.putHeader(name, hdr); + if(log.isTraceEnabled()) + log.trace(new StringBuilder().append(local_addr).append(" --> DATA(").append(dst).append(": #"). + append(seqno)); + if(entry.sent_msgs != null) + entry.sent_msgs.add(seqno, msg); // add *including* UnicastHeader, adds to retransmitter + entry.sent_msgs_seqno++; + } + catch(Throwable t) { + if(entry.sent_msgs != null) + entry.sent_msgs.ack(seqno); // remove seqno again, so it is not transmitted + if(t instanceof Error) + throw (Error)t; + if(t instanceof RuntimeException) + throw (RuntimeException)t; + else { + throw new RuntimeException("failure adding msg " + msg + " to the retransmit table", t); + } + } + } + // moved passing down of message out of the synchronized block: similar to NAKACK, we do *not* need + // to send unicast messages in order of sequence numbers because they will be sorted into the correct + // order at the receiver anyway. Of course, most of the time, the order will be correct (FIFO), so + // the cost of reordering is minimal. This is part of http://jira.jboss.com/jira/browse/JGRP-303 + try { // we catch the exception in this case because the msg is in the XMIT table and will be retransmitted + send(msg, evt); + } + catch(Throwable t) { + log.warn("failed sending the message", t); + } + return null; // we already passed the msg down + + case Event.VIEW_CHANGE: // remove connections to peers that are not members anymore ! + View view=(View)evt.getArg(); + Vector
        new_members=view.getMembers(); + Vector
        left_members; + synchronized(members) { + left_members=Util.determineLeftMembers(members, new_members); + members.clear(); + if(new_members != null) + members.addAll(new_members); + } + + // Remove all connections for members that left between the current view and the new view + // See DESIGN for details + boolean rc; + if(use_gms && !left_members.isEmpty()) { + Address mbr; + for(int i=0; i < left_members.size(); i++) { + mbr=left_members.elementAt(i); + rc=removeConnection(mbr); // adds to previous_members + if(rc && log.isTraceEnabled()) + log.trace("removed " + mbr + " from connection table, member(s) " + left_members + " left"); + } + } + // code by Matthias Weber May 23 2006 + for(Address mbr: previous_members) { + if(members.contains(mbr)) { + if(previous_members.remove(mbr)) { + if(log.isTraceEnabled()) + log.trace("removed " + mbr + " from previous_members as result of VIEW_CHANGE event, " + + "previous_members=" + previous_members); + } + } + } + // remove all members from enabled_members + synchronized(members) { + for(Address mbr: members) { + enabled_members.remove(mbr); + } + } + + synchronized(previous_members) { + for(Address mbr: previous_members) { + enabled_members.remove(mbr); + } + } + + // trash connections to/from members who are in the merge view, fix for: http://jira.jboss.com/jira/browse/JGRP-348 + // update (bela, April 25 2008): reverted because of http://jira.jboss.com/jira/browse/JGRP-659, we fix this + // in 2.7 only as we don't want to change the serialization format. The JIRA issue is + // http://jira.jboss.com/jira/browse/JGRP-742 +// if(view instanceof MergeView) { +// if(log.isTraceEnabled()) +// log.trace("removing all connections for the current members due to a merge"); +// removeConnections(members); +// } + + break; + + case Event.ENABLE_UNICASTS_TO: + Address member=(Address)evt.getArg(); + if(!enabled_members.contains(member)) + enabled_members.add(member); + boolean removed=previous_members.remove(member); + if(removed && log.isTraceEnabled()) + log.trace("removing " + member + " from previous_members as result of ENABLE_UNICAST_TO event, " + + "previous_members=" + previous_members); + break; + + case Event.DISABLE_UNICASTS_TO: + member=(Address)evt.getArg(); + removeConnection(member); + enabled_members.remove(member); + previous_members.remove(member); + break; + } + + return down_prot.down(evt); // Pass on to the layer below us + } + + + private void send(Message msg, Event evt) { + down_prot.down(evt); + num_msgs_sent++; + num_bytes_sent+=msg.getLength(); + } + + /** Removes and resets from connection table (which is already locked). Returns true if member was found, otherwise false */ + private boolean removeConnection(Address mbr) { + Entry entry; + + synchronized(connections) { + entry=connections.remove(mbr); + if(!previous_members.contains(mbr)) + previous_members.add(mbr); + } + if(entry != null) { + entry.reset(); + return true; + } + else + return false; + } + + + private void removeAllConnections() { + synchronized(connections) { + for(Entry entry: connections.values()) { + entry.reset(); + } + connections.clear(); + } + } + + + + /** Called by AckSenderWindow to resend messages for which no ACK has been received yet */ + public void retransmit(long seqno, Message msg) { + Object dst=msg.getDest(); + + // bela Dec 23 2002: + // this will remove a member on a MERGE request, e.g. A and B merge: when A sends the unicast + // request to B and there's a retransmit(), B will be removed ! + + // if(use_gms && !members.contains(dst) && !prev_members.contains(dst)) { + // + // if(log.isWarnEnabled()) log.warn("UNICAST.retransmit()", "seqno=" + seqno + ": dest " + dst + + // " is not member any longer; removing entry !"); + + // synchronized(connections) { + // removeConnection(dst); + // } + // return; + // } + + if(log.isTraceEnabled()) + log.trace("[" + local_addr + "] --> XMIT(" + dst + ": #" + seqno + ')'); + + down_prot.down(new Event(Event.MSG, msg)); + num_xmit_requests_received++; + } + + + + + + /** + * Check whether the hashtable contains an entry e for sender (create if not). If + * e.received_msgs is null and first is true: create a new AckReceiverWindow(seqno) and + * add message. Set e.received_msgs to the new window. Else just add the message. + * @return boolean True if we can send an ack, false otherwise + */ + private boolean handleDataReceived(Address sender, long seqno, Message msg) { + if(log.isTraceEnabled()) + log.trace(new StringBuilder().append(local_addr).append(" <-- DATA(").append(sender).append(": #").append(seqno)); + + if(previous_members.contains(sender)) { + // we don't want to see messages from departed members + if(seqno > DEFAULT_FIRST_SEQNO) { + if(log.isTraceEnabled()) + log.trace("discarding message " + seqno + " from previous member " + sender); + return false; // don't ack this message so the sender keeps resending it ! + } + if(log.isTraceEnabled()) + log.trace("removed " + sender + " from previous_members as we received a message from it"); + previous_members.remove(sender); + } + + Entry entry; + AckReceiverWindow win; + synchronized(connections) { + entry=connections.get(sender); + if(entry == null) { + entry=new Entry(); + connections.put(sender, entry); + if(log.isTraceEnabled()) + log.trace(local_addr + ": created new connection for dst " + sender); + } + win=entry.received_msgs; + if(win == null) { + win=new AckReceiverWindow(DEFAULT_FIRST_SEQNO); + entry.received_msgs=win; + } + } + + boolean added=win.add(seqno, msg); // entry.received_msgs is guaranteed to be non-null if we get here + boolean regular_msg_added=added && !msg.isFlagSet(Message.OOB); + num_msgs_received++; + num_bytes_received+=msg.getLength(); + + // http://jira.jboss.com/jira/browse/JGRP-713: // send the ack back *before* we process the message + // to limit unnecessary retransmits + if(immediate_ack) + sendAck(sender, seqno); // send an ack regardless of whether the message was added (stops retransmission) + + // message is passed up if OOB. Later, when remove() is called, we discard it. This affects ordering ! + // http://jira.jboss.com/jira/browse/JGRP-377 + if(msg.isFlagSet(Message.OOB)) { + if(added) + up_prot.up(new Event(Event.MSG, msg)); + win.removeOOBMessage(); // if we only have OOB messages, we'd never remove them ! + if(!(win.hasMessagesToRemove() && undelivered_msgs.get() > 0)) + return true; + } + + if(!added && !win.hasMessagesToRemove()) { // no ack if we didn't add the msg (e.g. duplicate) + return true; // ack the message, because this will stop retransmissions (which are unreliable) ! + } + + + // Try to remove (from the AckReceiverWindow) as many messages as possible as pass them up + Message m; + short removed_regular_msgs=0; + + // Prevents concurrent passing up of messages by different threads (http://jira.jboss.com/jira/browse/JGRP-198); + // this is all the more important once we have a threadless stack (http://jira.jboss.com/jira/browse/JGRP-181), + // where lots of threads can come up to this point concurrently, but only 1 is allowed to pass at a time + // We *can* deliver messages from *different* senders concurrently, e.g. reception of P1, Q1, P2, Q2 can result in + // delivery of P1, Q1, Q2, P2: FIFO (implemented by UNICAST) says messages need to be delivered only in the + // order in which they were sent by their senders + ReentrantLock lock=win.getLock(); + lock.lock(); // we don't block on entry any more (http://jira.jboss.com/jira/browse/JGRP-485) + try { + if(eager_lock_release) + locks.put(Thread.currentThread(), lock); + while((m=win.remove()) != null) { + // discard OOB msg as it has already been delivered (http://jira.jboss.com/jira/browse/JGRP-377) + if(m.isFlagSet(Message.OOB)) { + continue; + } + removed_regular_msgs++; + up_prot.up(new Event(Event.MSG, m)); + } + } + finally { + if(eager_lock_release) + locks.remove(Thread.currentThread()); + if(lock.isHeldByCurrentThread()) + lock.unlock(); + // We keep track of regular messages that we added, but couldn't remove (because of ordering). + // When we have such messages pending, then even OOB threads will remove and process them. + // http://jira.jboss.com/jira/browse/JGRP-780 + if(regular_msg_added && removed_regular_msgs == 0) { + undelivered_msgs.incrementAndGet(); + } + + if(removed_regular_msgs > 0) { // regardless of whether a message was added or not ! + int num_msgs_added=regular_msg_added? 1 : 0; + undelivered_msgs.addAndGet(-(removed_regular_msgs -num_msgs_added)); + } + } + return true; // msg was successfully received - send an ack back to the sender + } + + + /** Add the ACK to hashtable.sender.sent_msgs */ + private void handleAckReceived(Address sender, long seqno) { + Entry entry; + AckSenderWindow win; + + if(log.isTraceEnabled()) + log.trace(new StringBuilder().append(local_addr).append(" <-- ACK(").append(sender). + append(": #").append(seqno).append(')')); + synchronized(connections) { + entry=connections.get(sender); + } + if(entry == null || entry.sent_msgs == null) + return; + win=entry.sent_msgs; + win.ack(seqno); // removes message from retransmission + num_acks_received++; + } + + + + private void sendAck(Address dst, long seqno) { + Message ack=new Message(dst); + ack.setFlag(Message.OOB); + ack.putHeader(name, new UnicastHeader(UnicastHeader.ACK, seqno)); + if(log.isTraceEnabled()) + log.trace(new StringBuilder().append(local_addr).append(" --> ACK(").append(dst). + append(": #").append(seqno).append(')')); + down_prot.down(new Event(Event.MSG, ack)); + num_acks_sent++; + } + + + + + + + public static class UnicastHeader extends Header implements Streamable { + public static final byte DATA=0; + public static final byte ACK=1; + + byte type=DATA; + long seqno=0; + + static final int serialized_size=Global.BYTE_SIZE + Global.LONG_SIZE; + private static final long serialVersionUID=-5590873777959784299L; + + + public UnicastHeader() {} // used for externalization + + public UnicastHeader(byte type, long seqno) { + this.type=type; + this.seqno=seqno; + } + + public String toString() { + return "[UNICAST: " + type2Str(type) + ", seqno=" + seqno + ']'; + } + + public static String type2Str(byte t) { + switch(t) { + case DATA: return "DATA"; + case ACK: return "ACK"; + default: return ""; + } + } + + public final int size() { + return serialized_size; + } + + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeByte(type); + out.writeLong(seqno); + } + + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + type=in.readByte(); + seqno=in.readLong(); + } + + public void writeTo(DataOutputStream out) throws IOException { + out.writeByte(type); + out.writeLong(seqno); + } + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + type=in.readByte(); + seqno=in.readLong(); + } + } + + private static final class Entry { + AckReceiverWindow received_msgs=null; // stores all msgs rcvd by a certain peer in seqno-order + AckSenderWindow sent_msgs=null; // stores (and retransmits) msgs sent by us to a certain peer + long sent_msgs_seqno=DEFAULT_FIRST_SEQNO; // seqno for msgs sent by us + + void reset() { + if(sent_msgs != null) + sent_msgs.reset(); + if(received_msgs != null) + received_msgs.reset(); + sent_msgs_seqno=DEFAULT_FIRST_SEQNO; + } + + + public String toString() { + StringBuilder sb=new StringBuilder(); + if(sent_msgs != null) + sb.append("sent_msgs=").append(sent_msgs).append('\n'); + if(received_msgs != null) + sb.append("received_msgs=").append(received_msgs).append('\n'); + return sb.toString(); + } + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/UdpHeader.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/UdpHeader.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/UdpHeader.java 17 Aug 2012 14:51:08 -0000 1.1 @@ -0,0 +1,55 @@ +// $Id: UdpHeader.java,v 1.1 2012/08/17 14:51:08 marcin Exp $ + +package org.jgroups.protocols; + + +import org.jgroups.Header; +import org.jgroups.util.Streamable; + +import java.io.*; + + + + +public class UdpHeader extends Header implements Streamable { + public String channel_name=null; + int size=0; + + public UdpHeader() { + } // used for externalization + + public UdpHeader(String n) { + channel_name=n; + if(channel_name != null) + size=channel_name.length()+2; // +2 for writeUTF() + } + + public String toString() { + return "[UDP:channel_name=" + channel_name + ']'; + } + + + public int size() { + return size; + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeUTF(channel_name); + } + + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + channel_name=in.readUTF(); + } + + + public void writeTo(DataOutputStream out) throws IOException { + out.writeUTF(channel_name); + } + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + channel_name=in.readUTF(); + if(channel_name != null) + size=channel_name.length()+2; // +2 for writeUTF() + } +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/VERIFY_SUSPECT.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/VERIFY_SUSPECT.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/VERIFY_SUSPECT.java 17 Aug 2012 14:51:06 -0000 1.1 @@ -0,0 +1,381 @@ + +package org.jgroups.protocols; + +import org.jgroups.*; +import org.jgroups.stack.IpAddress; +import org.jgroups.stack.Protocol; +import org.jgroups.util.Streamable; +import org.jgroups.util.Util; + +import java.io.*; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.UnknownHostException; +import java.util.*; + + +/** + * Catches SUSPECT events traveling up the stack. Verifies that the suspected member is really dead. If yes, + * passes SUSPECT event up the stack, otherwise discards it. Has to be placed somewhere above the FD layer and + * below the GMS layer (receiver of the SUSPECT event). Note that SUSPECT events may be reordered by this protocol. + * @author Bela Ban + * @version $Id: VERIFY_SUSPECT.java,v 1.1 2012/08/17 14:51:06 marcin Exp $ + */ +public class VERIFY_SUSPECT extends Protocol implements Runnable { + private Address local_addr=null; + private long timeout=2000; // number of millisecs to wait for an are-you-dead msg + private int num_msgs=1; // number of are-you-alive msgs and i-am-not-dead responses (for redundancy) + final Hashtable suspects=new Hashtable(); // keys=Addresses, vals=time in mcses since added + private Thread timer=null; + private boolean use_icmp=false; // use InetAddress.isReachable() to double-check (rather than an are-you-alive msg) + private InetAddress bind_addr; // interface for ICMP pings + /** network interface to be used to send the ICMP packets */ + private NetworkInterface intf=null; + static final String name="VERIFY_SUSPECT"; + protected boolean shutting_down=false; + + + public String getName() { + return name; + } + + + public boolean setProperties(Properties props) { + super.setProperties(props); + + boolean ignore_systemprops=Util.isBindAddressPropertyIgnored(); + String str=Util.getProperty(new String[]{Global.BIND_ADDR, Global.BIND_ADDR_OLD}, props, "bind_addr", + ignore_systemprops, null); + if(str != null) { + try { + bind_addr=InetAddress.getByName(str); + } + catch(UnknownHostException unknown) { + if(log.isFatalEnabled()) log.fatal("(bind_addr): host " + str + " not known"); + return false; + } + props.remove("bind_addr"); + } + + str=props.getProperty("timeout"); + if(str != null) { + timeout=Long.parseLong(str); + props.remove("timeout"); + } + + str=props.getProperty("num_msgs"); + if(str != null) { + num_msgs=Integer.parseInt(str); + if(num_msgs <= 0) { + if(log.isWarnEnabled()) + log.warn("num_msgs is invalid (" + num_msgs + "): setting it to 1"); + num_msgs=1; + } + props.remove("num_msgs"); + } + + str=props.getProperty("use_icmp"); + if(str != null) { + use_icmp=Boolean.valueOf(str).booleanValue(); + props.remove("use_icmp"); + } + + if(!props.isEmpty()) { + log.error("the following properties are not recognized: " + props); + return false; + } + return true; + } + + + public Object down(Event evt) { + if(evt.getType() == Event.SHUTDOWN) { + shutting_down=true; + } + return down_prot.down(evt); + } + + public Object up(Event evt) { + switch(evt.getType()) { + + case Event.SET_LOCAL_ADDRESS: + local_addr=(Address)evt.getArg(); + shutting_down=false; + break; + + case Event.SUSPECT: // it all starts here ... + if(shutting_down) + return null; + Address suspected_mbr=(Address)evt.getArg(); + if(suspected_mbr == null) { + if(log.isErrorEnabled()) log.error("suspected member is null"); + return null; + } + + if(local_addr != null && local_addr.equals(suspected_mbr)) { + if(log.isTraceEnabled()) + log.trace("I was suspected; ignoring SUSPECT message"); + return null; + } + + if(!use_icmp) + verifySuspect(suspected_mbr); + else + verifySuspectWithICMP(suspected_mbr); + return null; // don't pass up; we will decide later (after verification) whether to pass it up + + + case Event.MSG: + Message msg=(Message)evt.getArg(); + VerifyHeader hdr=(VerifyHeader)msg.getHeader(name); + if(hdr == null) + break; + if(shutting_down) + return null; + switch(hdr.type) { + case VerifyHeader.ARE_YOU_DEAD: + if(hdr.from == null) { + if(log.isErrorEnabled()) log.error("ARE_YOU_DEAD: hdr.from is null"); + } + else { + Message rsp; + for(int i=0; i < num_msgs; i++) { + rsp=new Message(hdr.from, null, null); + rsp.setFlag(Message.OOB); + rsp.putHeader(name, new VerifyHeader(VerifyHeader.I_AM_NOT_DEAD, local_addr)); + down_prot.down(new Event(Event.MSG, rsp)); + } + } + return null; + case VerifyHeader.I_AM_NOT_DEAD: + if(hdr.from == null) { + if(log.isErrorEnabled()) log.error("I_AM_NOT_DEAD: hdr.from is null"); + return null; + } + unsuspect(hdr.from); + return null; + } + return null; + + + case Event.CONFIG: + if(bind_addr == null) { + Map config=(Map)evt.getArg(); + bind_addr=(InetAddress)config.get("bind_addr"); + } + } + return up_prot.up(evt); + } + + + /** + * Will be started when a suspect is added to the suspects hashtable. Continually iterates over the + * entries and removes entries whose time have elapsed. For each removed entry, a SUSPECT event is passed + * up the stack (because elapsed time means verification of member's liveness failed). Computes the shortest + * time to wait (min of all timeouts) and waits(time) msecs. Will be woken up when entry is removed (in case + * of successful verification of that member's liveness). Terminates when no entry remains in the hashtable. + */ + public void run() { + long val, diff; + + while(!suspects.isEmpty()) { + diff=0; + + List
        confirmed_suspects=new LinkedList
        (); + synchronized(suspects) { + for(Enumeration
        e=suspects.keys(); e.hasMoreElements();) { + Address mbr=e.nextElement(); + val=suspects.get(mbr).longValue(); + diff=System.currentTimeMillis() - val; + if(diff >= timeout) { // haven't been unsuspected, pass up SUSPECT + if(log.isTraceEnabled()) + log.trace("diff=" + diff + ", mbr " + mbr + " is dead (passing up SUSPECT event)"); + + confirmed_suspects.add(mbr); + suspects.remove(mbr); + continue; + } + diff=Math.max(diff, timeout - diff); + } + } + + for(Address suspect:confirmed_suspects) + up_prot.up(new Event(Event.SUSPECT,suspect)); + + if(diff > 0) + Util.sleep(diff); + } + } + + + + /* --------------------------------- Private Methods ----------------------------------- */ + + + /** + * Sends ARE_YOU_DEAD message to suspected_mbr, wait for return or timeout + */ + void verifySuspect(Address mbr) { + Message msg; + if(mbr == null) + return; + + synchronized(suspects) { + if(suspects.containsKey(mbr)) + return; + suspects.put(mbr, new Long(System.currentTimeMillis())); + } + //start timer before we send out are you dead messages + startTimer(); + + // moved out of synchronized statement (bela): http://jira.jboss.com/jira/browse/JGRP-302 + if(log.isTraceEnabled()) + log.trace("verifying that " + mbr + " is dead"); + + for(int i=0;i < num_msgs;i++) { + msg=new Message(mbr, null, null); + msg.setFlag(Message.OOB); + msg.putHeader(name, new VerifyHeader(VerifyHeader.ARE_YOU_DEAD, local_addr)); + down_prot.down(new Event(Event.MSG, msg)); + } + } + + + void verifySuspectWithICMP(Address suspected_mbr) { + InetAddress host=suspected_mbr instanceof IpAddress? ((IpAddress)suspected_mbr).getIpAddress() : null; + if(host == null) + throw new IllegalArgumentException("suspected_mbr is not of type IpAddress - FD_ICMP only works with these"); + try { + if(log.isTraceEnabled()) + log.trace("pinging host " + suspected_mbr + " using interface " + intf); + long start=System.currentTimeMillis(), stop; + boolean rc=host.isReachable(intf, 0, (int)timeout); + stop=System.currentTimeMillis(); + if(rc) { // success + if(log.isTraceEnabled()) + log.trace("successfully received response from " + host + " (after " + (stop-start) + "ms)"); + } + else { // failure + if(log.isTraceEnabled()) + log.debug("could not ping " + suspected_mbr + " after " + (stop-start) + "ms; " + + "passing up SUSPECT event"); + suspects.remove(suspected_mbr); + up_prot.up(new Event(Event.SUSPECT, suspected_mbr)); + } + } + catch(Exception ex) { + if(log.isErrorEnabled()) + log.error("failed pinging " + suspected_mbr, ex); + } + } + + void unsuspect(Address mbr) { + if(mbr == null) return; + boolean removed=false; + synchronized(suspects) { + if(suspects.containsKey(mbr)) { + if(log.isTraceEnabled()) log.trace("member " + mbr + " is not dead !"); + suspects.remove(mbr); + removed=true; + } + } + if(removed) { + down_prot.down(new Event(Event.UNSUSPECT, mbr)); + up_prot.up(new Event(Event.UNSUSPECT, mbr)); + } + } + + + private synchronized void startTimer() { + if(timer == null || !timer.isAlive()) { + timer=getThreadFactory().newThread(this,"VERIFY_SUSPECT.TimerThread"); + timer.setDaemon(true); + timer.start(); + } + } + + public void init() throws Exception { + super.init(); + if(bind_addr != null) + intf=NetworkInterface.getByInetAddress(bind_addr); + } + + public void start() throws Exception { + super.start(); + shutting_down=false; + } + + public synchronized void stop() { + Thread tmp; + if(timer != null && timer.isAlive()) { + tmp=timer; + timer=null; + tmp.interrupt(); + tmp=null; + } + timer=null; + } + /* ----------------------------- End of Private Methods -------------------------------- */ + + + + + + public static class VerifyHeader extends Header implements Streamable { + static final short ARE_YOU_DEAD=1; // 'from' is sender of verify msg + static final short I_AM_NOT_DEAD=2; // 'from' is suspected member + + short type=ARE_YOU_DEAD; + Address from=null; // member who wants to verify that suspected_mbr is dead + + + public VerifyHeader() { + } // used for externalization + + VerifyHeader(short type) { + this.type=type; + } + + VerifyHeader(short type, Address from) { + this(type); + this.from=from; + } + + + public String toString() { + switch(type) { + case ARE_YOU_DEAD: + return "[VERIFY_SUSPECT: ARE_YOU_DEAD]"; + case I_AM_NOT_DEAD: + return "[VERIFY_SUSPECT: I_AM_NOT_DEAD]"; + default: + return "[VERIFY_SUSPECT: unknown type (" + type + ")]"; + } + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeShort(type); + out.writeObject(from); + } + + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + type=in.readShort(); + from=(Address)in.readObject(); + } + + public void writeTo(DataOutputStream out) throws IOException { + out.writeShort(type); + Util.writeAddress(from, out); + } + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + type=in.readShort(); + from=Util.readAddress(in); + } + + } + + +} + Index: 3rdParty_sources/jgroups/org/jgroups/protocols/VIEW_SYNC.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/VIEW_SYNC.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/VIEW_SYNC.java 17 Aug 2012 14:51:08 -0000 1.1 @@ -0,0 +1,438 @@ +package org.jgroups.protocols; + + +import org.jgroups.*; +import org.jgroups.protocols.pbcast.GMS; +import org.jgroups.annotations.GuardedBy; +import org.jgroups.stack.Protocol; +import org.jgroups.util.Streamable; +import org.jgroups.util.TimeScheduler; +import org.jgroups.util.Util; + +import java.io.*; +import java.util.Properties; +import java.util.Vector; +import java.util.concurrent.Future; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + + +/** + * Periodically sends the view to the group. When a view is received which is greater than the current view, we + * install it. Otherwise we simply discard it. This is used to solve the problem for unreliable view + * dissemination outlined in JGroups/doc/ReliableViewInstallation.txt. This protocol is supposed to be just below GMS. + * @author Bela Ban + * @version $Id: VIEW_SYNC.java,v 1.1 2012/08/17 14:51:08 marcin Exp $ + */ +public class VIEW_SYNC extends Protocol { + Address local_addr=null; + final Vector
        mbrs=new Vector
        (); + View my_view=null; + ViewId my_vid=null; + + /** Sends a VIEW_SYNC message to the group every 20 seconds on average. 0 disables sending of VIEW_SYNC messages */ + long avg_send_interval=60000; + + private int num_views_sent=0; + private int num_view_requests_sent=0; + private int num_view_responses_seen=0; + private int num_views_non_local=0; + private int num_views_equal=0; + private int num_views_less=0; + private int num_views_adjusted=0; + private long last_view_request_sent=0; + + @GuardedBy("view_task_lock") + private Future view_send_task_future=null; // bcasts periodic view sync message (added to timer below) + + private final Lock view_task_lock=new ReentrantLock(); + + TimeScheduler timer=null; + static final String name="VIEW_SYNC"; + + + + public String getName() { + return name; + } + + public long getAverageSendInterval() { + return avg_send_interval; + } + + public void setAverageSendInterval(long gossip_interval) { + avg_send_interval=gossip_interval; + } + + public int getNumViewsSent() { + return num_views_sent; + } + + public int getNumViewRequestsSent() { + return num_view_requests_sent; + } + + public int getNumViewResponsesSeen() { + return num_view_requests_sent; + } + + public int getNumViewsNonLocal() { + return num_views_non_local; + } + + public int getNumViewsLess() { + return num_views_less; + } + + public int getNumViewsEqual() { + return num_views_equal; + } + + public long getLastViewRequestSent() { + return last_view_request_sent; + } + + public int getNumViewsAdjusted() { + return num_views_adjusted; + } + + public void resetStats() { + super.resetStats(); + num_views_adjusted=num_views_sent=0; + } + + + + public boolean setProperties(Properties props) { + String str; + + super.setProperties(props); + + str=props.getProperty("avg_send_interval"); + if(str != null) { + avg_send_interval=Long.parseLong(str); + props.remove("avg_send_interval"); + } + + if(!props.isEmpty()) { + log.error("these properties are not recognized: " + props); + return false; + } + return true; + } + + + public void start() throws Exception { + timer=getTransport().getTimer(); + if(timer == null) + throw new Exception("timer cannot be retrieved from protocol stack"); + } + + public void stop() { + stopViewSender(); + } + + /** Sends a VIEW_SYNC_REQ to all members, every member replies with a VIEW multicast */ + public void sendViewRequest() { + Message msg=new Message(null); + msg.setFlag(Message.OOB); + ViewSyncHeader hdr=new ViewSyncHeader(ViewSyncHeader.VIEW_SYNC_REQ, null); + msg.putHeader(name, hdr); + down_prot.down(new Event(Event.MSG, msg)); + num_view_requests_sent++; + last_view_request_sent=System.currentTimeMillis(); + } + +// public void sendFakeViewForTestingOnly() { +// ViewId fake_vid=new ViewId(local_addr, my_vid.getId() +2); +// View fake_view=new View(fake_vid, new Vector(my_view.getMembers())); +// System.out.println("sending fake view " + fake_view); +// my_view=fake_view; +// my_vid=fake_vid; +// sendView(); +// } + + + public Object up(Event evt) { + Message msg; + ViewSyncHeader hdr; + int type=evt.getType(); + + switch(type) { + + case Event.MSG: + msg=(Message)evt.getArg(); + hdr=(ViewSyncHeader)msg.getHeader(name); + if(hdr == null) + break; + Address sender=msg.getSrc(); + switch(hdr.type) { + case ViewSyncHeader.VIEW_SYNC: + handleView(hdr.view, sender); + break; + case ViewSyncHeader.VIEW_SYNC_REQ: + if(!sender.equals(local_addr)) + sendView(); + break; + default: + if(log.isErrorEnabled()) log.error("ViewSyncHeader type " + hdr.type + " not known"); + } + return null; + + case Event.VIEW_CHANGE: + View view=(View)evt.getArg(); + handleViewChange(view); + break; + + case Event.SET_LOCAL_ADDRESS: + local_addr=(Address)evt.getArg(); + break; + } + + return up_prot.up(evt); + } + + + + public Object down(Event evt) { + switch(evt.getType()) { + case Event.VIEW_CHANGE: + View v=(View)evt.getArg(); + handleViewChange(v); + break; + } + return down_prot.down(evt); + } + + + + /* --------------------------------------- Private Methods ---------------------------------------- */ + + private void handleView(View v, Address sender) { + num_view_responses_seen++; + Vector
        members=v.getMembers(); + if(!members.contains(local_addr)) { + if(log.isWarnEnabled()) + log.warn("discarding view as I (" + local_addr + ") am not member of view (" + v + ")"); + num_views_non_local++; + return; + } + + ViewId vid=v.getVid(); + int rc=vid.compareTo(my_vid); + if(rc > 0) { // foreign view is greater than my own view; update my own view ! + if(log.isTraceEnabled()) + log.trace("view from " + sender + " (" + vid + ") is greater than my own view (" + my_vid + ");" + + " will update my own view"); + + Message view_change=new Message(local_addr, local_addr, null); + org.jgroups.protocols.pbcast.GMS.GmsHeader hdr; + hdr=new org.jgroups.protocols.pbcast.GMS.GmsHeader(org.jgroups.protocols.pbcast.GMS.GmsHeader.VIEW, v); + view_change.putHeader(GMS.name, hdr); + up_prot.up(new Event(Event.MSG, view_change)); + num_views_adjusted++; + } else if (rc == 0) { + if (log.isTraceEnabled()) + log.trace("view from " + sender + " (" + vid + ") is same as my own view; ignoring"); + num_views_equal++; + } else { + if (log.isTraceEnabled()) + log.trace("view from " + sender + " (" + vid + ") is less than my own view (" + my_vid + "); ignoring"); + num_views_less++; + } + } + + private void handleViewChange(View view) { + Vector
        tmp=view.getMembers(); + if(tmp != null) { + mbrs.clear(); + mbrs.addAll(tmp); + } + my_view=(View)view.clone(); + my_vid=my_view.getVid(); + if(my_view.size() > 1) { + startViewSender(); + } + else { + stopViewSender(); + } + } + + private void sendView() { + View tmp=(View)(my_view != null? my_view.clone() : null); + if(tmp == null) return; + Message msg=new Message(null); // send to the group + msg.setFlag(Message.OOB); + ViewSyncHeader hdr=new ViewSyncHeader(ViewSyncHeader.VIEW_SYNC, tmp); + msg.putHeader(name, hdr); + down_prot.down(new Event(Event.MSG, msg)); + num_views_sent++; + } + + /** Starts with view_task_lock held, no need to acquire it again */ + void startViewSender() { + try { + view_task_lock.lock(); + if(view_send_task_future == null || view_send_task_future.isDone()) { + ViewSendTask view_send_task=new ViewSendTask(); + view_send_task_future=timer.scheduleWithDynamicInterval(view_send_task, true); // fixed-rate scheduling + if(log.isTraceEnabled()) + log.trace("view send task started"); + } + } + finally { + view_task_lock.unlock(); + } + } + + + void stopViewSender() { + try { + view_task_lock.lock(); + if(view_send_task_future != null) { + view_send_task_future.cancel(false); + view_send_task_future=null; + if(log.isTraceEnabled()) + log.trace("view send task stopped"); + } + } + finally { + view_task_lock.unlock(); + } + } + + + + + + + /* ------------------------------------End of Private Methods ------------------------------------- */ + + + + + + + + public static class ViewSyncHeader extends Header implements Streamable { + public static final int VIEW_SYNC = 1; // contains a view + public static final int VIEW_SYNC_REQ = 2; // request to all members to send their views + + int type=0; + View view=null; + + public ViewSyncHeader() { + } + + + public ViewSyncHeader(int type, View view) { + this.type=type; + this.view=view; + } + + public int getType() { + return type; + } + + public View getView() { + return view; + } + + static String type2String(int t) { + switch(t) { + case VIEW_SYNC: + return "VIEW_SYNC"; + case VIEW_SYNC_REQ: + return "VIEW_SYNC_REQ"; + default: + return ""; + } + } + + public String toString() { + StringBuilder sb=new StringBuilder("[").append(type2String(type)).append("]"); + if(view != null) + sb.append(", view= ").append(view); + return sb.toString(); + } + + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeInt(type); + if(view == null) { + out.writeBoolean(false); + return; + } + out.writeBoolean(true); + view.writeExternal(out); + } + + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + type=in.readInt(); + boolean available=in.readBoolean(); + if(available) { + view=new View(); + view.readExternal(in); + } + } + + public int size() { + int retval=Global.INT_SIZE + Global.BYTE_SIZE + Global.BYTE_SIZE; // type + view type + presence for digest + if(view != null) + retval+=view.serializedSize(); + return retval; + } + + public void writeTo(DataOutputStream out) throws IOException { + out.writeInt(type); + // 0 == null, 1 == View, 2 == MergeView + byte b=(byte)(view == null? 0 : (view instanceof MergeView? 2 : 1)); + out.writeByte(b); + Util.writeStreamable(view, out); + } + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + type=in.readInt(); + byte b=in.readByte(); + Class clazz=b == 2? MergeView.class : View.class; + view=(View)Util.readStreamable(clazz, in); + } + + + } + + + + + /** + Periodically multicasts a View_SYNC message + */ + private class ViewSendTask implements TimeScheduler.Task { + + public long nextInterval() { + long interval=computeSleepTime(); + if(interval <= 0) + return 10000; + else + return interval; + } + + + public void run() { + sendView(); + } + + long computeSleepTime() { + int num_mbrs=Math.max(mbrs.size(), 1); + return getRandom((num_mbrs * avg_send_interval * 2)); + } + + long getRandom(long range) { + return (long)((Math.random() * range) % range); + } + } + + + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/package.html =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/package.html,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/package.html 17 Aug 2012 14:51:06 -0000 1.1 @@ -0,0 +1,6 @@ + + + Provides implementations of transport protocols which are + responsible for sending and receiving messages to/from the network. + + \ No newline at end of file Index: 3rdParty_sources/jgroups/org/jgroups/protocols/obsolete/ENCRYPT.java.txt =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/obsolete/ENCRYPT.java.txt,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/obsolete/ENCRYPT.java.txt 17 Aug 2012 14:51:27 -0000 1.1 @@ -0,0 +1,408 @@ +// $Id: ENCRYPT.java.txt,v 1.1 2012/08/17 14:51:27 marcin Exp $ + +package org.jgroups.protocols; + +import java.io.Serializable; +import java.security.*; +import java.security.spec.KeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Properties; +import java.util.Vector; +import org.jgroups.*; +import org.jgroups.stack.*; +import org.jgroups.log.Trace; + + + +class EncryptHeader implements Serializable { + int type; + static final int ENCRYPT = 0; + static final int KEY_REQUEST = 1; + static final int SERVER_PUBKEY = 2; + static final int SECRETKEY = 3; + static final int SECRETKEY_READY = 4; + public EncryptHeader(int type) { + this.type = type; + } + public String toString() {return "[ENCTYPT: ]";} +} + + +/** + * ENCRYPT layer. Encrypt and decrypt the group communication in JGroups + */ +public class ENCRYPT extends Protocol { + Address local_addr=null; + Address keyServerAddr = null; + boolean keyServer=false; + String asymAlgorithm="RSA"; + String symAlgorithm="DES/ECB/PKCS5Padding"; + int asymInit=512; // initial public/private key length + int symInit=56; // initial shared key length + // for public/private Key + KeyPair Kpair; // to store own's public/private Key + SecretKey desKey=null; + PublicKey pubKey = null; // for server to store the temporary client public key + PublicKey serverPubKey = null; // for client to store server's public Key + Cipher cipher; + Cipher rsa; + Vector members=new Vector(); + Vector notReady = new Vector(); + + public ENCRYPT(){ + Security.addProvider(new ABAProvider()); + } + + + public String getName() {return "ENCRYPT";} + + + /* + * GetAlgorithm: Get the algorithm name from "algorithm/mode/padding" + */ + private String getAlgorithm(String s) + { + int index = s.indexOf("/"); + if (index==-1) + return s; + + return s.substring(0, index); + } + + + public boolean setProperties(Properties props) {super.setProperties(props); + String str; + this.props=props; + + // asymmetric key length + str=props.getProperty("asymInit"); + if (str != null) { + asymInit = new Integer(str).intValue(); + props.remove("asymInit"); + System.out.println("asymInit = "+asymInit); + } + + // symmetric key length + str=props.getProperty("symInit"); + if (str != null) { + symInit = new Integer(str).intValue(); + props.remove("symInit"); + System.out.println("symInit = "+symInit); + } + + // asymmetric algorithm name + str=props.getProperty("asymAlgorithm"); + if (str != null) { + asymAlgorithm = new String(str).toString(); + props.remove("asymAlgorithm"); + } + + // symmetric algorithm name + str=props.getProperty("symAlgorithm"); + if (str != null) { + symAlgorithm = new String(str).toString(); + props.remove("symAlgorithm"); + } + if (props.size() > 0) { + log.error("ENCRYPT.setProperties(): these properties are not recognized: " + props); + + return false; + } + + // generate keys according to the specified algorithms + try{ + // generate publicKey and Private Key using RSA + KeyPairGenerator KpairGen = KeyPairGenerator.getInstance(getAlgorithm(asymAlgorithm)); + KpairGen.initialize(asymInit, new SecureRandom()); + Kpair = KpairGen.generateKeyPair(); + + // generate secret key + KeyGenerator keyGen = KeyGenerator.getInstance(getAlgorithm(symAlgorithm)); + keyGen.init(symInit); + desKey = keyGen.generateKey(); + + // initialize for rsa, cipher encryption/decryption + rsa = Cipher.getInstance(asymAlgorithm); + cipher = Cipher.getInstance(symAlgorithm); + } + catch(Exception e){ + System.out.println(e+"at setProperties"); + } + return true; + } + + + /** Just remove if you don't need to reset any state */ + public void reset() {} + + public void up(Event evt) { + Message msg; + Message newMsg; + EncryptHeader hdr; + switch(evt.getType()) { + + case Event.SET_LOCAL_ADDRESS: + local_addr=(Address)evt.getArg(); + break; + case Event.FIND_INITIAL_MBRS_OK: + Vector member=(Vector)evt.getArg(); + keyServer = member.size() > 0 ? false : true; + + if (member != null && member.size() > 0) + keyServerAddr = (Address)((PingRsp)member.firstElement()).coord_addr; + else keyServerAddr = local_addr; + + System.out.println("keyServer = " + keyServer + " keyServerAddr : "+keyServerAddr.toString()); + if (!keyServer) + { + desKey = null; + // client send clien's public key to server and request server's public key + newMsg = new Message(keyServerAddr,local_addr,Kpair.getPublic().getEncoded()); + newMsg.addHeader(new EncryptHeader(EncryptHeader.KEY_REQUEST)); + passDown(new Event(Event.MSG,newMsg)); + } + + passUp(evt); + return; + + case Event.MSG: + msg=(Message)evt.getArg(); + + Object obj=msg.peekHeader(); + + // if not encrypted message, pass up + if (obj == null || !(obj instanceof EncryptHeader)) { + passUp(evt); + return; + } + hdr = (EncryptHeader)msg.removeHeader(); + + switch(hdr.type){ + + // key request from client and send server's public key to client + case EncryptHeader.KEY_REQUEST: + try{ + // store the this client to notReady list using client's address + notReady.addElement(msg.getSrc()); + // store the client's public key for temporary + PublicKey pubKey = generatePubKey(msg.getBuffer()); + + // send server's publicKey + newMsg = new Message(msg.getSrc(), local_addr, Kpair.getPublic().getEncoded()); + newMsg.addHeader(new EncryptHeader(EncryptHeader.SERVER_PUBKEY)); + passDown(new Event(Event.MSG, newMsg)); + + // send shared DesKey to client + // 1. Decrypt desKey with server's own private Key + // 2. Encrypt decrypted desKey with client's own public Key + // encrypt encoded desKey using server's private key + rsa.init(Cipher.ENCRYPT_MODE, Kpair.getPrivate()); + byte [] decryptedKey = rsa.doFinal(desKey.getEncoded()); + + // encrypt decrypted key using client's public key + rsa.init(Cipher.ENCRYPT_MODE, pubKey); + byte [] encryptedKey = rsa.doFinal(decryptedKey); + + //send encrypted deskey to client + newMsg = new Message(msg.getSrc(), local_addr, encryptedKey); + newMsg.addHeader(new EncryptHeader(EncryptHeader.SECRETKEY)); + passDown(new Event(Event.MSG, newMsg)); + } + catch(Exception e){ + System.out.println(e+"0"); + } + return; + + case EncryptHeader.SECRETKEY_READY: + //server get client's public key and generate the secret key + notReady.removeElement(msg.getSrc()); + return; + case EncryptHeader.SERVER_PUBKEY: + serverPubKey = generatePubKey(msg.getBuffer()); + return; + + case EncryptHeader.SECRETKEY: + try{ + // decrypt using client's private Key + rsa.init(Cipher.DECRYPT_MODE,Kpair.getPrivate()); + byte[] decryptedKey = rsa.doFinal(msg.getBuffer()); + + // decrypt using server's public Key + rsa.init(Cipher.DECRYPT_MODE,serverPubKey); + byte[] encodedKey = rsa.doFinal(decryptedKey); + + // decode secretKey + desKey = decodedKey(encodedKey); + System.out.println("Client generate shared secret key"); + // send ready message + newMsg = new Message(msg.getSrc(), local_addr, null); + newMsg.addHeader(new EncryptHeader(EncryptHeader.SECRETKEY_READY)); + passDown(new Event(Event.MSG, newMsg)); + } + catch(Exception e){ + System.out.println(e+"5"); + } + return; + + default: break; + } + + if (hdr.type != 0) System.out.println("This is ERROR"); + + // not have shared key yet + // this encrypted message is of no use, drop it + if (desKey == null) return; + + // if both the shared key and incoming message are not null + // decrypt the message + if (msg.getBuffer()!=null) + { + try{ + cipher.init(Cipher.DECRYPT_MODE, desKey); + msg.setBuffer(cipher.doFinal(msg.getBuffer())); + } + catch(Exception e){ + System.out.println(e+"6"); + } + } + + break; + } + passUp(evt); // Pass up to the layer above us + } + + public void down(Event evt) { + Message msg; + Message newMsg; + SecretKey key; + boolean leave = false; + + switch(evt.getType()) { + case Event.VIEW_CHANGE: + Vector new_members=(Vector)((View)evt.getArg()).getMembers(); + + // member size decreases: member leaves, need a new key + if (members.size() > new_members.size()) leave = true; + + // copy member list + synchronized(members) { + members.removeAllElements(); + if (new_members != null && new_members.size() > 0) + for (int i=0; i < new_members.size(); i++) + members.addElement(new_members.elementAt(i)); + } + + // redistribute/regain the new key because old member leaves + if (leave){ + // get coordinator address + Object obj = members.firstElement(); + + // if I'm the coordinator/key-server + if (obj.equals(local_addr)){ + //create the new shared key and distribute + keyServer = true; + keyServerAddr = local_addr; + + // reset shared key + desKey=null; + + try { + //generate new shared key + KeyGenerator keyGen = KeyGenerator.getInstance(getAlgorithm(symAlgorithm)); + keyGen.init(symInit); + desKey = keyGen.generateKey(); + } + catch (Exception e) { + System.out.println(e+"7"); + } + + }//end of local_addr == obj + // if I'm not the coordinator/key-server + else { + keyServer = false; + keyServerAddr = (Address)obj; + + // reset shared key + desKey = null; + + // client send clien's public key to server and request server's public key + newMsg = new Message(keyServerAddr, local_addr, Kpair.getPublic().getEncoded()); + newMsg.addHeader(new EncryptHeader(EncryptHeader.KEY_REQUEST)); + passDown(new Event(Event.MSG, newMsg)); + System.out.println("Request new key"); + } + } + break; + + case Event.MSG: + msg=(Message)evt.getArg(); + int i; + + // For Server: + // if some members don't have the shared key yet + if (!notReady.isEmpty()){ + System.out.println("not Ready list :"+ notReady.toString()); + if (msg.getDest() == null){ + for (i = 0; i < notReady.size();i++){ + newMsg = new Message(notReady.elementAt(i), local_addr, msg.getBuffer()); + passDown(new Event(Event.MSG, newMsg)); + } + break; + } + else{ + for (i = 0; i < notReady.size();i++){ + if (msg.getDest() == notReady.elementAt(i)){ + passDown(evt); + return; + } + } + } + } + + // I already know the shared key + if (desKey != null) { + try { + // if the message is not empty, encrypt it + if (msg.getBuffer() != null) + { + cipher.init(Cipher.ENCRYPT_MODE, desKey); + msg.setBuffer(cipher.doFinal(msg.getBuffer())); + msg.addHeader(new EncryptHeader(0)); + } + } + catch (Exception e) { + System.out.println(e+"8"); + } + } + break; + } + //System.out.println("Pass Down: "+evt.toString()); + passDown(evt); // Pass on to the layer below us + } + + private SecretKey decodedKey(byte[] encodedKey){ + SecretKey key = null; + try{ + SecretKeyFactory KeyFac = SecretKeyFactory.getInstance(getAlgorithm(symAlgorithm)); + SecretKeySpec desKeySpec = new SecretKeySpec(encodedKey, getAlgorithm(symAlgorithm)); + key = KeyFac.generateSecret((KeySpec)desKeySpec); + } + catch(Exception e){ + log.error(e); + } + return key; + } + + private PublicKey generatePubKey(byte [] encodedKey){ + PublicKey pubKey = null; + try{ + KeyFactory KeyFac = KeyFactory.getInstance(getAlgorithm(asymAlgorithm)); + X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(encodedKey); + pubKey = KeyFac.generatePublic((KeySpec)x509KeySpec); + } + catch(Exception e){ + log.error(e); + } + return pubKey; + } +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/obsolete/ENCRYPT1_4.java.txt =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/obsolete/ENCRYPT1_4.java.txt,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/obsolete/ENCRYPT1_4.java.txt 17 Aug 2012 14:51:27 -0000 1.1 @@ -0,0 +1,554 @@ +// changes made by mandar + +// Added .* imports +// replacing SecretKey with SecretKey + + +// $Id: ENCRYPT1_4.java.txt,v 1.1 2012/08/17 14:51:27 marcin Exp $ + +package org.jgroups.protocols.obsolete; + +import org.jgroups.Address; +import org.jgroups.Event; +import org.jgroups.Message; +import org.jgroups.View; +import org.jgroups.protocols.PingRsp; +import org.jgroups.stack.Protocol; + +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.SecretKeySpec; +import java.io.IOException; +import java.security.*; +import java.security.spec.X509EncodedKeySpec; +import java.util.Properties; +import java.util.Vector; + +/** + * ENCRYPT1_4 layer. Encrypt and decrypt the group communication in JGroups + */ +public class ENCRYPT1_4 extends Protocol { + +public static class EncryptHeader extends org.jgroups.Header { + int type; + static final int ENCRYPT=0; + static final int KEY_REQUEST=1; + static final int SERVER_PUBKEY=2; + static final int SECRETKEY=3; + static final int SECRETKEY_READY=4; + + // adding key for Message object purpose + static final String KEY="encrypt"; + + public EncryptHeader(){} + + public EncryptHeader(int type) { + this.type=type; + } + + public void writeExternal(java.io.ObjectOutput out) throws IOException { + out.writeInt(type); + } + + public void readExternal(java.io.ObjectInput in) throws IOException, ClassNotFoundException { + type=in.readInt(); + } + + public String toString() { + return "[ENCTYPT: ]"; + } +} + + + Address local_addr=null; + Address keyServerAddr=null; + boolean keyServer=false; + String asymAlgorithm="RSA"; + String symAlgorithm="DES/ECB/PKCS5Padding"; + int asymInit=512; // initial public/private key length + int symInit=56; // initial shared key length + // for public/private Key + KeyPair Kpair; // to store own's public/private Key + SecretKey desKey=null; + final PublicKey pubKey=null; // for server to store the temporary client public key + PublicKey serverPubKey=null; // for client to store server's public Key + Cipher cipher; + Cipher rsa; + final Vector members=new Vector(); + final Vector notReady=new Vector(); + + public ENCRYPT1_4() { + //Provider prov = Security.getProvider("SUN"); + //Security.addProvider(prov); + } + + + public String getName() { + return "ENCRYPT1_4"; + } + + + /* + * GetAlgorithm: Get the algorithm name from "algorithm/mode/padding" + */ + private static String getAlgorithm(String s) { + int index=s.indexOf("/"); + if(index == -1) + return s; + + return s.substring(0, index); + } + + + public boolean setProperties(Properties props) { + String str; + + super.setProperties(props); + // asymmetric key length + str=props.getProperty("asymInit"); + if(str != null) { + asymInit=Integer.parseInt(str); + props.remove("asymInit"); + + if(log.isInfoEnabled()) log.info("Asym algo bits used is " + asymInit); + } + + // symmetric key length + str=props.getProperty("symInit"); + if(str != null) { + symInit=Integer.parseInt(str); + props.remove("symInit"); + + if(log.isInfoEnabled()) log.info("Sym algo bits used is " + symInit); + } + + // asymmetric algorithm name + str=props.getProperty("asymAlgorithm"); + if(str != null) { + asymAlgorithm=str; + props.remove("asymAlgorithm"); + + if(log.isInfoEnabled()) log.info("Asym algo used is " + asymAlgorithm); + } + + // symmetric algorithm name + str=props.getProperty("symAlgorithm"); + if(str != null) { + symAlgorithm=str; + props.remove("symAlgorithm"); + + if(log.isInfoEnabled()) log.info("Sym algo used is " + symAlgorithm); + } + if(props.size() > 0) { + + if(log.isErrorEnabled()) log.error("these properties are not recognized: " + props); + return false; + } + + + return true; + } + + public void init() throws Exception { + // generate keys according to the specified algorithms + // generate publicKey and Private Key using RSA + KeyPairGenerator KpairGen=KeyPairGenerator.getInstance(getAlgorithm(asymAlgorithm)); + KpairGen.initialize(asymInit, new SecureRandom()); + Kpair=KpairGen.generateKeyPair(); + + // generate secret key + KeyGenerator keyGen=KeyGenerator.getInstance(getAlgorithm(symAlgorithm)); + keyGen.init(symInit); + desKey=keyGen.generateKey(); + + // initialize for rsa, cipher encryption/decryption + rsa=Cipher.getInstance(asymAlgorithm); + cipher=Cipher.getInstance(symAlgorithm); + + + if(log.isInfoEnabled()) log.info(" Both asym and sym algo initialized with the single shared key"); + } + + /** Just remove if you don't need to reset any state */ + public static void reset() { + } + + public void up(Event evt) { + Message msg; + Message newMsg; + EncryptHeader hdr; + + + if(log.isInfoEnabled()) log.info("Event going up is " + evt); + + switch(evt.getType()) { + case Event.SET_LOCAL_ADDRESS: + + if(log.isInfoEnabled()) log.info("Set address call"); + local_addr=(Address)evt.getArg(); + break; + case Event.FIND_INITIAL_MBRS_OK: + Vector member=(Vector)evt.getArg(); + + if(log.isInfoEnabled()) log.info("FIND_INIT members call, left members are " + member.size()); + + + // this check is required, to prevent keyServer= false when adding itself + if (!keyServer) + keyServer=member.size() <= 0; + + if(member != null && member.size() > 0) + keyServerAddr=((PingRsp) member.firstElement()).coord_addr; + else + keyServerAddr=local_addr; + + if(!keyServer) + { + + desKey=null; + + if(log.isDebugEnabled()) log.debug("This is not keyserver, deskey set to null"); + // client send clien's public key to server and request server's public key + newMsg=new Message(keyServerAddr, local_addr, Kpair.getPublic().getEncoded()); + // making changes (MANDAR) + newMsg.putHeader(EncryptHeader.KEY, new EncryptHeader(EncryptHeader.KEY_REQUEST)); + passDown(new Event(Event.MSG, newMsg)); + } + + if(log.isInfoEnabled()) log.info("Done parsing for encrypt headers, sending upwards" + evt); + passUp(evt); + return; + + case Event.MSG: + msg=(Message) evt.getArg(); + + if(log.isInfoEnabled()) log.info("This is a message from peer, not control header" + msg); + + // making changes (MANDAR) + if(msg == null) { + + if(log.isDebugEnabled()) log.debug("Null message"); + passUp(evt); + return; + } + + // making changes (MANDAR) + //Object obj=msg.peekHeader(); + Object obj=msg.removeHeader(EncryptHeader.KEY); + + if(log.isInfoEnabled()) log.info("Stripping the required protocol header"); + + // if not encrypted message, pass up + if(obj == null || !(obj instanceof EncryptHeader)) { + + if(log.isInfoEnabled()) log.info("Dropping package as ENCRYPT1_4 protocol is not been recognized, msg will not be passed up"); + + // BELA comment this out in case U think otherwise + //passUp(evt); + return; + } + + // making changes (MANDAR) + //hdr = (EncryptHeader)msg.removeHeader(); + hdr=(EncryptHeader) obj; + + if(log.isInfoEnabled()) log.info("Header received " + hdr + ':' + hdr.type); + switch(hdr.type) { + // key request from client and send server's public key to client + case EncryptHeader.KEY_REQUEST: + try { + + if(log.isDebugEnabled()) log.debug("Request for key"); + // store the this client to notReady list using client's address + notReady.addElement(msg.getSrc()); + // store the client's public key for temporary + PublicKey tmpPubKey=generatePubKey(msg.getBuffer()); + + if(log.isDebugEnabled()) log.debug("Generated requestors public key"); + + // send server's publicKey + newMsg=new Message(msg.getSrc(), local_addr, Kpair.getPublic().getEncoded()); + // making changes (MANDAR) + newMsg.putHeader(EncryptHeader.KEY, new EncryptHeader(EncryptHeader.SERVER_PUBKEY)); + + if(log.isDebugEnabled()) log.debug("Encoded servers public key using clients public key, only client can debug it using its private key and sending it back"); + passDown(new Event(Event.MSG, newMsg)); + + // my changes (MANDAR) + rsa.init(Cipher.ENCRYPT_MODE, tmpPubKey); + byte[] encryptedKey = rsa.doFinal(desKey.getEncoded()); + + if(log.isDebugEnabled()) log.debug(" Generated encoded key which only client can decode"); + + // send shared DesKey to client + // 1. Decrypt desKey with server's own private Key + // 2. Encrypt decrypted desKey with client's own public Key + // encrypt encoded desKey using server's private key + /* + rsa.init(Cipher.ENCRYPT_MODE, Kpair.getPrivate()); + byte[] decryptedKey=rsa.doFinal(desKey.getEncoded()); + + // encrypt decrypted key using client's public key + rsa.init(Cipher.ENCRYPT_MODE, pubKey); + byte[] encryptedKey=rsa.doFinal(decryptedKey); + */ + //send encrypted deskey to client + newMsg=new Message(msg.getSrc(), local_addr, encryptedKey); + // making changes (MANDAR) + newMsg.putHeader(EncryptHeader.KEY, new EncryptHeader(EncryptHeader.SECRETKEY)); + + if(log.isDebugEnabled()) log.debug(" Sending encoded key to client"); + passDown(new Event(Event.MSG, newMsg)); + } + catch(Exception e) { + e.printStackTrace(); + System.out.println(e + "0"); + } + return; + case EncryptHeader.SECRETKEY_READY: + //server get client's public key and generate the secret key + notReady.removeElement(msg.getSrc()); + + if(log.isDebugEnabled()) log.debug("Removed client " + msg.getSrc() + "from notready list"); + return; + case EncryptHeader.SERVER_PUBKEY: + serverPubKey=generatePubKey(msg.getBuffer()); + + if(log.isDebugEnabled()) log.debug(" Obtained the servers public key"); + return; + + case EncryptHeader.SECRETKEY: + try { + // decrypt using client's private Key + rsa.init(Cipher.DECRYPT_MODE, Kpair.getPrivate()); + // my changes (MANDAR) + byte[] encodedKey = rsa.doFinal(msg.getBuffer()); + + + if(log.isDebugEnabled()) log.debug("generating encoded key obtained from server-admin"); + + /* Piece commented out by MANDAR + byte[] decryptedKey=rsa.doFinal(msg.getBuffer()); + // decrypt using server's public Key + rsa.init(Cipher.DECRYPT_MODE, serverPubKey); + byte[] encodedKey=rsa.doFinal(decryptedKey); + */ + + // decode secretKey + desKey=decodedKey(encodedKey); + if(desKey == null) + log.error("ohh oh !! DES key is null"); + + + // send ready message (MANDAR) null -> "" + newMsg=new Message(msg.getSrc(), local_addr, null); + // making changes (MANDAR) + newMsg.putHeader(EncryptHeader.KEY, new EncryptHeader(EncryptHeader.SECRETKEY_READY)); + passDown(new Event(Event.MSG, newMsg)); + + if(log.isDebugEnabled()) log.debug("Got the deskey, sending down sec_Ready header"); + } + catch(Exception e) { + e.printStackTrace(); + System.out.println(e + "5"); + } + return; + + default: + break; + } + + if (hdr.type != 0) + log.error("Error , header is not 0"); + + // not have shared key yet + // this encrypted message is of no use, drop it + if(desKey == null) return; + + + if(log.isInfoEnabled()) log.info(" Starting to decypher messages"); + + // if both the shared key and incoming message are not null + // decrypt the message + if(msg.getBuffer() != null) { + try { + cipher.init(Cipher.DECRYPT_MODE, desKey); + msg.setBuffer(cipher.doFinal(msg.getBuffer())); + } + catch(Exception e) { + e.printStackTrace(); + } + } + break; + } + + if(log.isInfoEnabled()) log.info("Passing up event"); + passUp(evt); // Pass up to the layer above us + } + + public void down(Event evt) { + Message msg; + Message newMsg; + boolean leave=false; + + + if(log.isInfoEnabled()) log.info("down:evt is " + evt + ':' + evt.getType()); + + switch(evt.getType()) { + + case Event.VIEW_CHANGE: + + if(log.isInfoEnabled()) log.info("View change call, new member coming in"); + Vector new_members=((View)evt.getArg()).getMembers(); + + // member size decreases: member leaves, need a new key + if(members.size() > new_members.size()) leave=true; + + // copy member list + synchronized(members) { + members.removeAllElements(); + if(new_members != null && new_members.size() > 0) + for(int i=0; i < new_members.size(); i++) + members.addElement(new_members.elementAt(i)); + }// end of sync + + // redistribute/regain the new key because old member leaves + if(leave) { + // get coordinator address + Object obj=members.firstElement(); + + // if I'm the coordinator/key-server + if(obj.equals(local_addr)) { + //create the new shared key and distribute + keyServer=true; + keyServerAddr=local_addr; + + // reset shared key + desKey=null; + + if(log.isInfoEnabled()) log.info(" leave caused deskey to be null "); + + try { + //generate new shared key + KeyGenerator keyGen=KeyGenerator.getInstance(getAlgorithm(symAlgorithm)); + keyGen.init(symInit); + desKey=keyGen.generateKey(); + } + catch(Exception e) { + e.printStackTrace(); + } + }//end of local_addr == obj + // if I'm not the coordinator/key-server + else { + keyServer=false; + keyServerAddr=(Address)obj; + + // reset shared key + desKey=null; + + // client send clien's public key to server and request server's public key + newMsg=new Message(keyServerAddr, local_addr, Kpair.getPublic().getEncoded()); + // making changes (MANDAR) + newMsg.putHeader(EncryptHeader.KEY, new EncryptHeader(EncryptHeader.KEY_REQUEST)); + passDown(new Event(Event.MSG, newMsg)); + + if(log.isDebugEnabled()) log.debug("Requesting new key to be part of group"); + } // end of else + } + break; + + case Event.MSG: + msg= (Message) evt.getArg(); + + if(log.isDebugEnabled()) log.debug("Its a message call " + msg); + int i; + + // For Server: + // if some members don't have the shared key yet + if(!notReady.isEmpty()) + { + System.out.println("not Ready list :" + notReady.toString()); + if(msg.getDest() == null) { + for(i=0; i < notReady.size(); i++) { + // making changes (MANDAR) + newMsg=new Message((Address)notReady.elementAt(i), local_addr, msg.getBuffer()); + passDown(new Event(Event.MSG, newMsg)); + } + break; + } + else + { + for(i=0; i < notReady.size(); i++) { + if(msg.getDest().equals(notReady.elementAt(i))) { + passDown(evt); + return; + }// end of if.. + }// end of for.. + }// end of else + } + + // I already know the shared key + if(desKey != null) + { + + + if(log.isInfoEnabled()) log.info("DESkey is not null, I know it "); + try + { + // if the message is not empty, encrypt it + if(msg.getBuffer() != null) + { + cipher.init(Cipher.ENCRYPT_MODE, desKey); + msg.setBuffer(cipher.doFinal(msg.getBuffer())); + msg.putHeader(EncryptHeader.KEY, new EncryptHeader(0)); + + if(log.isInfoEnabled()) log.info(" have DES key , package sent"); + } + else + { + msg.setBuffer(null); + msg.putHeader(EncryptHeader.KEY, new EncryptHeader(0)); + + if(log.isInfoEnabled()) log.info(" buffer null, added header"); + } + }catch(Exception e) + { + e.printStackTrace(); + } + } + break; + }// check des key.. + + if(log.isInfoEnabled()) log.info("Pass Down: " + evt.toString()); + passDown(evt); // Pass on to the layer below us + } + + private SecretKey decodedKey(byte[] encodedKey) { + SecretKey key=null; + try { + //change needed mandar + SecretKeyFactory KeyFac=SecretKeyFactory.getInstance(getAlgorithm(symAlgorithm)); + SecretKeySpec desKeySpec=new SecretKeySpec(encodedKey, getAlgorithm(symAlgorithm)); + key=KeyFac.generateSecret(desKeySpec); + } + catch(Exception e) { + e.printStackTrace(); + } + return key; + } + + private PublicKey generatePubKey(byte[] encodedKey) { + PublicKey tmpPubKey=null; + try { + KeyFactory KeyFac=KeyFactory.getInstance(getAlgorithm(asymAlgorithm)); + X509EncodedKeySpec x509KeySpec=new X509EncodedKeySpec(encodedKey); + tmpPubKey=KeyFac.generatePublic(x509KeySpec); + } + catch(Exception e) { + e.printStackTrace(); + } + return tmpPubKey; + } +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/obsolete/FC.java.txt =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/obsolete/FC.java.txt,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/obsolete/FC.java.txt 17 Aug 2012 14:51:27 -0000 1.1 @@ -0,0 +1,643 @@ +// $Id: FC.java.txt,v 1.1 2012/08/17 14:51:27 marcin Exp $ + +package org.jgroups.protocols; + +import EDU.oswego.cs.dl.util.concurrent.ConcurrentReaderHashMap; +import org.jgroups.*; +import org.jgroups.stack.Protocol; +import org.jgroups.util.BoundedList; +import org.jgroups.util.CondVar; +import org.jgroups.util.Streamable; + +import java.io.*; +import java.util.*; + +/** + * Simple flow control protocol based on a credit system. Each sender has a number of credits (bytes + * to send). When the credits have been exhausted, the sender blocks. Each receiver also keeps track of + * how many credits it has received from a sender. When credits for a sender fall below a threshold, + * the receiver sends more credits to the sender. Works for both unicast and multicast messages. + *

        + * Note that this protocol must be located towards the top of the stack, or all down_threads from JChannel to this + * protocol must be set to false ! This is in order to block JChannel.send()/JChannel.down(). + * @author Bela Ban + * @version $Revision: 1.1 $ + */ +public class FC extends Protocol { + + /** My own address */ + Address local_addr=null; + + /** HashMap: keys are members, values are credits left. For each send, the + * number of credits is decremented by the message size */ + final Map sent=new HashMap(11); + // final Map sent=new ConcurrentHashMap(11); + + /** HashMap: keys are members, values are credits left (in bytes). + * For each receive, the credits for the sender are decremented by the size of the received message. + * When the credits are 0, we refill and send a CREDIT message to the sender. Sender blocks until CREDIT + * is received after reaching min_credits credits. */ + final Map received=new ConcurrentReaderHashMap(11); + // final Map received=new ConcurrentHashMap(11); + + /** We cache the membership */ + final Vector members=new Vector(11); + + /** List of members from whom we expect credits */ + final Vector creditors=new Vector(11); + + /** Max number of bytes to send per receiver until an ack must + * be received before continuing sending */ + private long max_credits=50000; + + /** Max time (in milliseconds) to block. If credit hasn't been received after max_block_time, we send + * a REPLENISHMENT request to the members from which we expect credits. A value <= 0 means to + * wait forever. + */ + private long max_block_time=5000; + + /** If credits fall below this limit, we send more credits to the sender. (We also send when + * credits are exhausted (0 credits left)) */ + double min_threshold=0.25; + + /** Computed as max_credits times min_theshold. If explicitly set, this will + * override the above computation */ + private long min_credits=0; + + /** Current blocking. True if blocking, else false */ + private final CondVar blocking=new CondVar("blocking", Boolean.FALSE); + + static final String name="FC"; + + private long start_blocking=0, stop_blocking=0; + + private int num_blockings=0, num_replenishments=0, num_credit_requests=0; + private long total_time_blocking=0; + + final BoundedList last_blockings=new BoundedList(50); + + final static FcHeader REPLENISH_HDR=new FcHeader(FcHeader.REPLENISH); + final static FcHeader CREDIT_REQUEST_HDR=new FcHeader(FcHeader.CREDIT_REQUEST); + + + + public String getName() { + return name; + } + + public void resetStats() { + super.resetStats(); + num_blockings=num_replenishments=num_credit_requests=0; + total_time_blocking=0; + last_blockings.removeAll(); + } + + public long getMaxCredits() { + return max_credits; + } + + public void setMaxCredits(long max_credits) { + this.max_credits=max_credits; + } + + public double getMinThreshold() { + return min_threshold; + } + + public void setMinThreshold(double min_threshold) { + this.min_threshold=min_threshold; + } + + public long getMinCredits() { + return min_credits; + } + + public void setMinCredits(long min_credits) { + this.min_credits=min_credits; + } + + public boolean isBlocked() { + Object obj=blocking.get(); + return obj != null && obj instanceof Boolean && ((Boolean)obj).booleanValue(); + } + + public int getNumberOfBlockings() { + return num_blockings; + } + + public long getTotalTimeBlocked() { + return total_time_blocking; + } + + public double getAverageTimeBlocked() { + return num_blockings == 0? num_blockings : total_time_blocking / num_blockings; + } + + public int getNumberOfReplenishmentsReceived() { + return num_replenishments; + } + + public int getNumberOfCreditRequests() { + return num_credit_requests; + } + + public String printSenderCredits() { + return printMap(sent); + } + + public String printReceiverCredits() { + return printMap(received); + } + + public String printCredits() { + StringBuilder sb=new StringBuilder(); + sb.append("senders:\n").append(printMap(sent)).append("\n\nreceivers:\n").append(printMap(received)); + return sb.toString(); + } + + public Map dumpStats() { + Map retval=super.dumpStats(); + if(retval == null) + retval=new HashMap(); + retval.put("senders", printMap(sent)); + retval.put("receivers", printMap(received)); + retval.put("num_blockings", new Integer(this.num_blockings)); + retval.put("avg_time_blocked", new Double(getAverageTimeBlocked())); + retval.put("num_replenishments", new Integer(this.num_replenishments)); + return retval; + } + + public String showLastBlockingTimes() { + return last_blockings.toString(); + } + + + + public void unblock() { + unblockSender(); + } + + + + public boolean setProperties(Properties props) { + String str; + boolean min_credits_set=false; + + super.setProperties(props); + str=props.getProperty("max_credits"); + if(str != null) { + max_credits=Long.parseLong(str); + props.remove("max_credits"); + } + + str=props.getProperty("min_threshold"); + if(str != null) { + min_threshold=Double.parseDouble(str); + props.remove("min_threshold"); + } + + str=props.getProperty("min_credits"); + if(str != null) { + min_credits=Long.parseLong(str); + props.remove("min_credits"); + min_credits_set=true; + } + + if(!min_credits_set) + min_credits=(long)((double)max_credits * min_threshold); + + str=props.getProperty("max_block_time"); + if(str != null) { + max_block_time=Long.parseLong(str); + props.remove("max_block_time"); + } + + if(props.size() > 0) { + log.error("FC.setProperties(): the following properties are not recognized: " + props); + + return false; + } + return true; + } + + public void stop() { + super.stop(); + unblock(); + } + + + /** + * We need to receive view changes concurrent to messages on the down events: a message might blocks, e.g. + * because we don't have enough credits to send to member P. However, if member P crashed, we need to unblock ! + * @param evt + */ + protected void receiveDownEvent(Event evt) { + if(evt.getType() == Event.VIEW_CHANGE) { + View v=(View)evt.getArg(); + Vector mbrs=v.getMembers(); + handleViewChange(mbrs); + } + super.receiveDownEvent(evt); + } + + public void down(Event evt) { + switch(evt.getType()) { + case Event.MSG: + handleDownMessage(evt); + return; + } + passDown(evt); // this could potentially use the lower protocol's thread which may block + } + + + private synchronized void handleDownMessage(Event evt) { + if(Boolean.TRUE.equals(blocking.get())) { // blocked + waitUntilEnoughCreditsAvailable(); + } + else { + // not blocked + boolean rc; + synchronized(sent) { // 'sent' is the same lock as blocking.getLock()... + rc=decrMessage((Message)evt.getArg()); + if(rc == false) { + if(trace) + log.trace("blocking due to insufficient credits"); + blocking.set(Boolean.TRUE); + start_blocking=System.currentTimeMillis(); + num_blockings++; + } + } + if(rc == false) { + waitUntilEnoughCreditsAvailable(); + } + } + + passDown(evt); + } + + + + public void up(Event evt) { + switch(evt.getType()) { + case Event.SET_LOCAL_ADDRESS: + local_addr=(Address)evt.getArg(); + break; + case Event.VIEW_CHANGE: + handleViewChange(((View)evt.getArg()).getMembers()); + break; + case Event.MSG: + Message msg=(Message)evt.getArg(); + FcHeader hdr=(FcHeader)msg.removeHeader(name); + if(hdr != null) { + switch(hdr.type) { + case FcHeader.REPLENISH: + num_replenishments++; + handleCredit(msg.getSrc()); + break; + case FcHeader.CREDIT_REQUEST: + num_credit_requests++; + Address sender=msg.getSrc(); + if(trace) + log.trace("received credit request from " + sender + ": sending credits"); + received.put(sender, new Long(max_credits)); + sendCredit(sender); + break; + default: + log.error("header type " + hdr.type + " not known"); + break; + } + return; // don't pass message up + } + else { + adjustCredit(msg); + } + break; + } + passUp(evt); + } + + + + private void handleCredit(Address sender) { + if(sender == null) return; + StringBuilder sb=null; + boolean unblock=false; + + if(trace) { + Long old_credit=(Long)sent.get(sender); + sb=new StringBuilder(); + sb.append("received credit from ").append(sender).append(", old credit was "). + append(old_credit).append(", new credits are ").append(max_credits). + append(".\nCreditors before are: ").append(creditors); + } + + synchronized(sent) { + sent.put(sender, new Long(max_credits)); + if(creditors.size() > 0) { // we are blocked because we expect credit from one or more members + removeCreditor(sender); + if(trace) { + sb.append("\nCreditors after removal of ").append(sender).append(" are: ").append(creditors); + log.trace(sb.toString()); + } + if(creditors.size() == 0) { + unblock=true; + } + } + else { // no creditors, but still blocking: we need to unblock + if(Boolean.TRUE.equals(blocking.get())) + unblock=true; + } + } + if(unblock) // moved this outside of the 'sent' synchronized block + unblockSender(); + } + + + /** + * Check whether sender has enough credits left. If not, send him some more + * @param msg + */ + private void adjustCredit(Message msg) { + Address src=msg.getSrc(); + long size=Math.max(24, msg.getLength()); + + if(src == null) { + if(log.isErrorEnabled()) log.error("src is null"); + return; + } + + if(decrementCredit(received, src, size, min_credits) == false) { + received.put(src, new Long(max_credits)); + if(trace) log.trace("sending replenishment message to " + src); + sendCredit(src); + } + } + + + + private void sendCredit(Address dest) { + Message msg=new Message(dest, null, null); + msg.putHeader(name, REPLENISH_HDR); + passDown(new Event(Event.MSG, msg)); + } + + private void sendCreditRequest(final Address dest) { + Message msg=new Message(dest, null, null); + msg.putHeader(name, CREDIT_REQUEST_HDR); + passDown(new Event(Event.MSG, msg)); + } + + + + /** + * Checks whether enough credits are available to send message. If not, blocks until enough credits + * are available + * @param evt Guaranteed to be a Message + * @return + */ + private void waitUntilEnoughCreditsAvailable() { + while(true) { + try { + blocking.waitUntilWithTimeout(Boolean.FALSE, max_block_time); // waits on 'sent' + break; + } + catch(TimeoutException e) { + List tmp=new ArrayList(creditors); + if(trace) + log.trace("timeout occurred waiting for credits; sending credit request to " + tmp + + ", creditors are " + creditors); + Address mbr; + for(Iterator it=tmp.iterator(); it.hasNext();) { + mbr=(Address)it.next(); + sendCreditRequest(mbr); + } + } + } + } + + + /** + * Try to decrement the credits needed for this message and return true if successful, or false otherwise. + * For unicast destinations, the credits required are subtracted from the unicast destination member, for + * multicast messages the credits are subtracted from all current members in the group. + * @param msg + * @return false: will block, true: will not block + */ + private boolean decrMessage(Message msg) { + Address dest; + long size; + boolean success=true; + + // ****************************************************************************************************** + // this method is called by waitUntilEnoughCredits() which syncs on 'sent', so we don't need to sync here + // ****************************************************************************************************** + + if(msg == null) { + if(log.isErrorEnabled()) log.error("msg is null"); + return true; // don't block ! + } + dest=msg.getDest(); + size=Math.max(24, msg.getLength()); + if(dest != null && !dest.isMulticastAddress()) { // unicast destination + if(decrementCredit(sent, dest, size, 0)) { + return true; + } + else { + addCreditor(dest); + return false; + } + } + else { // multicast destination + for(Iterator it=members.iterator(); it.hasNext();) { + dest=(Address)it.next(); + if(decrementCredit(sent, dest, size, 0) == false) { + addCreditor(dest); + success=false; + } + } + } + return success; + } + + + + + /** If message queueing is enabled, sends queued messages and unlocks sender (if successful) */ + private void unblockSender() { + if(start_blocking > 0) { + stop_blocking=System.currentTimeMillis(); + long diff=stop_blocking - start_blocking; + total_time_blocking+=diff; + last_blockings.add(new Long(diff)); + stop_blocking=start_blocking=0; + if(trace) + log.trace("setting blocking=false, blocking time was " + diff + "ms"); + } + if(trace) + log.trace("setting blocking=false"); + blocking.set(Boolean.FALSE); + } + + + private void addCreditor(Address mbr) { + if(mbr != null && !creditors.contains(mbr)) + creditors.add(mbr); + } + + private void removeCreditor(Address mbr) { + creditors.remove(mbr); + } + + + + + /** + * Find the credits associated with dest and decrement its credits by credits_required. If the remaining + * value is less than or equal to 0, return false, else return true. Note that we will always subtract the credits. + * @param map + * @param dest + * @param credits_required Number of bytes required + * @param minimal_credits For the receiver: add minimal credits to check whether credits need to be sent + * @return Whether the required credits could successfully be subtracted from the credits left + */ + private boolean decrementCredit(Map map, Address dest, long credits_required, long minimal_credits) { + long credits_left, new_credits_left; + Long tmp=(Long)map.get(dest); + boolean success; + + if(tmp == null) + return true; + + credits_left=tmp.longValue(); + success=credits_left > (credits_required + minimal_credits); + new_credits_left=Math.max(0, credits_left - credits_required); + map.put(dest, new Long(new_credits_left)); + + if(success) { + return true; + } + else { + if(trace) { + StringBuilder sb=new StringBuilder(); + sb.append("not enough credits left for ").append(dest).append(": left=").append(new_credits_left); + sb.append(", required+min_credits=").append((credits_required +min_credits)).append(", required="); + sb.append(credits_required).append(", min_credits=").append(min_credits); + log.trace(sb.toString()); + } + return false; + } + } + + + void handleViewChange(Vector mbrs) { + Address addr; + if(mbrs == null) return; + + if(trace) log.trace("new membership: " + mbrs); + members.clear(); + members.addAll(mbrs); + + synchronized(received) { + // add members not in membership to received hashmap (with full credits) + for(int i=0; i < mbrs.size(); i++) { + addr=(Address) mbrs.elementAt(i); + if(!received.containsKey(addr)) + received.put(addr, new Long(max_credits)); + } + // remove members that left + for(Iterator it=received.keySet().iterator(); it.hasNext();) { + addr=(Address) it.next(); + if(!mbrs.contains(addr)) + it.remove(); + } + } + + boolean unblock=false; + synchronized(sent) { + // add members not in membership to sent hashmap (with full credits) + for(int i=0; i < mbrs.size(); i++) { + addr=(Address) mbrs.elementAt(i); + if(!sent.containsKey(addr)) + sent.put(addr, new Long(max_credits)); + } + // remove members that left + for(Iterator it=sent.keySet().iterator(); it.hasNext();) { + addr=(Address)it.next(); + if(!mbrs.contains(addr)) + it.remove(); // modified the underlying map + } + + // remove all creditors which are not in the new view + for(int i=0; i < creditors.size(); i++) { + Address creditor=(Address)creditors.elementAt(i); + if(!mbrs.contains(creditor)) + creditors.remove(creditor); + } + + if(trace) log.trace("creditors are " + creditors); + if(creditors.size() == 0) + unblock=true; + } + if(unblock) + unblockSender(); + } + + private static String printMap(Map m) { + Map.Entry entry; + StringBuilder sb=new StringBuilder(); + for(Iterator it=m.entrySet().iterator(); it.hasNext();) { + entry=(Map.Entry)it.next(); + sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); + } + return sb.toString(); + } + + + + + + public static class FcHeader extends Header implements Streamable { + public static final byte REPLENISH = 1; + public static final byte CREDIT_REQUEST = 2; // the sender of the message is the requester + + byte type = REPLENISH; + + public FcHeader() { + + } + + public FcHeader(byte type) { + this.type=type; + } + + public long size() { + return Global.BYTE_SIZE; + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeByte(type); + } + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + type=in.readByte(); + } + + public void writeTo(DataOutputStream out) throws IOException { + out.writeByte(type); + } + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + type=in.readByte(); + } + + public String toString() { + switch(type) { + case REPLENISH: return "REPLENISH"; + case CREDIT_REQUEST: return "CREDIT_REQUEST"; + default: return ""; + } + } + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/obsolete/FD_PROB.java.txt =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/obsolete/FD_PROB.java.txt,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/obsolete/FD_PROB.java.txt 17 Aug 2012 14:51:27 -0000 1.1 @@ -0,0 +1,649 @@ +// $Id: FD_PROB.java.txt,v 1.1 2012/08/17 14:51:27 marcin Exp $ + +package org.jgroups.protocols; + +import org.jgroups.*; +import org.jgroups.stack.Protocol; +import org.jgroups.util.Util; +import org.jgroups.util.Streamable; + +import java.io.*; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Properties; +import java.util.Vector; + + +/** + * Probabilistic failure detection protocol based on "A Gossip-Style Failure Detection Service" + * by Renesse, Minsky and Hayden.

        + * Each member maintains a list of all other members: for each member P, 2 data are maintained, a heartbeat + * counter and the time of the last increment of the counter. Each member periodically sends its own heartbeat + * counter list to a randomly chosen member Q. Q updates its own heartbeat counter list and the associated + * time (if counter was incremented). Each member periodically increments its own counter. If, when sending + * its heartbeat counter list, a member P detects that another member Q's heartbeat counter was not incremented + * for timeout seconds, Q will be suspected.

        + * This protocol can be used both with a PBCAST *and* regular stacks. + * @author Bela Ban 1999 + * @version $Revision: 1.1 $ + */ +public class FD_PROB extends Protocol implements Runnable { + Address local_addr=null; + Thread hb=null; + long timeout=3000; // before a member with a non updated timestamp is suspected + long gossip_interval=1000; + Vector members=null; + final Hashtable counters=new Hashtable(); // keys=Addresses, vals=FdEntries + final Hashtable invalid_pingers=new Hashtable(); // keys=Address, vals=Integer (number of pings from suspected mbrs) + int max_tries=2; // number of times to send a are-you-alive msg (tot time= max_tries*timeout) + + + public String getName() { + return "FD_PROB"; + } + + + public boolean setProperties(Properties props) { + String str; + + super.setProperties(props); + str=props.getProperty("timeout"); + if(str != null) { + timeout=Long.parseLong(str); + props.remove("timeout"); + } + + str=props.getProperty("gossip_interval"); + if(str != null) { + gossip_interval=Long.parseLong(str); + props.remove("gossip_interval"); + } + + str=props.getProperty("max_tries"); + if(str != null) { + max_tries=Integer.parseInt(str); + props.remove("max_tries"); + } + + if(props.size() > 0) { + log.error("FD_PROB.setProperties(): the following properties are not recognized: " + props); + + return false; + } + return true; + } + + + public void start() throws Exception { + if(hb == null) { + hb=new Thread(this, "FD_PROB.HeartbeatThread"); + hb.setDaemon(true); + hb.start(); + } + } + + + public void stop() { + Thread tmp=null; + if(hb != null && hb.isAlive()) { + tmp=hb; + hb=null; + tmp.interrupt(); + try { + tmp.join(timeout); + } + catch(Exception ex) { + } + } + hb=null; + } + + + public Object up(Event evt) { + Message msg; + FdHeader hdr=null; + Object obj; + + switch(evt.getType()) { + + case Event.SET_LOCAL_ADDRESS: + local_addr=(Address) evt.getArg(); + break; + + case Event.MSG: + msg=(Message) evt.getArg(); + obj=msg.getHeader(getName()); + if(obj == null || !(obj instanceof FdHeader)) { + updateCounter(msg.getSrc()); // got a msg from this guy, reset its time (we heard from it now) + break; + } + + hdr=(FdHeader) msg.removeHeader(getName()); + switch(hdr.type) { + case FdHeader.HEARTBEAT: // heartbeat request; send heartbeat ack + if(checkPingerValidity(msg.getSrc()) == false) // false == sender of heartbeat is not a member + return; + + // 2. Update my own array of counters + + if(log.isInfoEnabled()) log.info("<-- HEARTBEAT from " + msg.getSrc()); + updateCounters(hdr); + return; // don't pass up ! + case FdHeader.NOT_MEMBER: + if(warn) log.warn("NOT_MEMBER: I'm being shunned; exiting"); + passUp(new Event(Event.EXIT)); + return; + default: + if(warn) log.warn("FdHeader type " + hdr.type + " not known"); + return; + } + } + passUp(evt); // pass up to the layer above us + } + + + public Object down(Event evt) { + int num_mbrs; + Vector excluded_mbrs; + FdEntry entry; + Address mbr; + + switch(evt.getType()) { + + // Start heartbeat thread when we have more than 1 member; stop it when membership drops below 2 + case Event.VIEW_CHANGE: + passDown(evt); + synchronized(this) { + View v=(View) evt.getArg(); + + // mark excluded members + excluded_mbrs=computeExcludedMembers(members, v.getMembers()); + if(excluded_mbrs != null && excluded_mbrs.size() > 0) { + for(int i=0; i < excluded_mbrs.size(); i++) { + mbr=(Address) excluded_mbrs.elementAt(i); + entry=(FdEntry) counters.get(mbr); + if(entry != null) + entry.setExcluded(true); + } + } + + members=v != null ? v.getMembers() : null; + if(members != null) { + num_mbrs=members.size(); + if(num_mbrs >= 2) { + if(hb == null) { + try { + start(); + } + catch(Exception ex) { + if(warn) log.warn("exception when calling start(): " + ex); + } + } + } + else + stop(); + } + } + break; + + default: + passDown(evt); + break; + } + } + + + /** + Loop while more than 1 member available. Choose a member randomly (not myself !) and send a + heartbeat. Wait for ack. If ack not received withing timeout, mcast SUSPECT message. + */ + public void run() { + Message hb_msg; + FdHeader hdr; + Address hb_dest, key; + FdEntry entry; + long curr_time, diff; + + + + if(log.isInfoEnabled()) log.info("heartbeat thread was started"); + + while(hb != null && members.size() > 1) { + + // 1. Get a random member P (excluding ourself) + hb_dest=getHeartbeatDest(); + if(hb_dest == null) { + if(warn) log.warn("hb_dest is null"); + Util.sleep(gossip_interval); + continue; + } + + + // 2. Increment own counter + entry=(FdEntry) counters.get(local_addr); + if(entry == null) { + entry=new FdEntry(); + counters.put(local_addr, entry); + } + entry.incrementCounter(); + + + // 3. Send heartbeat to P + hdr=createHeader(); + if(hdr == null) + if(warn) log.warn("header could not be created. Heartbeat will not be sent"); + else { + hb_msg=new Message(hb_dest, null, null); + hb_msg.putHeader(getName(), hdr); + + if(log.isInfoEnabled()) log.info("--> HEARTBEAT to " + hb_dest); + passDown(new Event(Event.MSG, hb_msg)); + } + + + if(log.isInfoEnabled()) log.info("own counters are " + printCounters()); + + + // 4. Suspect members from which we haven't heard for timeout msecs + for(Enumeration e=counters.keys(); e.hasMoreElements();) { + curr_time=System.currentTimeMillis(); + key=(Address) e.nextElement(); + entry=(FdEntry) counters.get(key); + + if(entry.getTimestamp() > 0 && (diff=curr_time - entry.getTimestamp()) >= timeout) { + if(entry.excluded()) { + if(diff >= 2 * timeout) { // remove members marked as 'excluded' after 2*timeout msecs + counters.remove(key); + if(log.isInfoEnabled()) log.info("removed " + key); + } + } + else { + if(log.isInfoEnabled()) log.info("suspecting " + key); + passUp(new Event(Event.SUSPECT, key)); + } + } + } + Util.sleep(gossip_interval); + } // end while + + + if(log.isInfoEnabled()) log.info("heartbeat thread was stopped"); + } + + + + + + + + /* -------------------------------- Private Methods ------------------------------- */ + + Address getHeartbeatDest() { + Address retval=null; + int r, size; + Vector members_copy; + + if(members == null || members.size() < 2 || local_addr == null) + return null; + members_copy=(Vector) members.clone(); + members_copy.removeElement(local_addr); // don't select myself as heartbeat destination + size=members_copy.size(); + r=((int) (Math.random() * (size + 1))) % size; + retval=(Address) members_copy.elementAt(r); + return retval; + } + + + /** Create a header containing the counters for all members */ + FdHeader createHeader() { + int num_mbrs=counters.size(), index=0; + FdHeader ret=null; + Address key; + FdEntry entry; + + if(num_mbrs <= 0) + return null; + ret=new FdHeader(FdHeader.HEARTBEAT, num_mbrs); + for(Enumeration e=counters.keys(); e.hasMoreElements();) { + key=(Address) e.nextElement(); + entry=(FdEntry) counters.get(key); + if(entry.excluded()) + continue; + if(index >= ret.members.length) { + if(warn) log.warn("index " + index + " is out of bounds (" + + ret.members.length + ')'); + break; + } + ret.members[index]=key; + ret.counters[index]=entry.getCounter(); + index++; + } + return ret; + } + + + /** Set my own counters values to max(own-counter, counter) */ + void updateCounters(FdHeader hdr) { + Address key; + FdEntry entry; + + if(hdr == null || hdr.members == null || hdr.counters == null) { + if(warn) log.warn("hdr is null or contains no counters"); + return; + } + + for(int i=0; i < hdr.members.length; i++) { + key=hdr.members[i]; + if(key == null) continue; + entry=(FdEntry) counters.get(key); + if(entry == null) { + entry=new FdEntry(hdr.counters[i]); + counters.put(key, entry); + continue; + } + + if(entry.excluded()) + continue; + + // only update counter (and adjust timestamp) if new counter is greater then old one + entry.setCounter(Math.max(entry.getCounter(), hdr.counters[i])); + } + } + + + /** Resets the counter for mbr */ + void updateCounter(Address mbr) { + FdEntry entry; + + if(mbr == null) return; + entry=(FdEntry) counters.get(mbr); + if(entry != null) + entry.setTimestamp(); + } + + + String printCounters() { + StringBuilder sb=new StringBuilder(); + Address mbr; + FdEntry entry; + + for(Enumeration e=counters.keys(); e.hasMoreElements();) { + mbr=(Address) e.nextElement(); + entry=(FdEntry) counters.get(mbr); + sb.append("\n" + mbr + ": " + entry._toString()); + } + return sb.toString(); + } + + + static Vector computeExcludedMembers(Vector old_mbrship, Vector new_mbrship) { + Vector ret=new Vector(); + if(old_mbrship == null || new_mbrship == null) return ret; + for(int i=0; i < old_mbrship.size(); i++) + if(!new_mbrship.contains(old_mbrship.elementAt(i))) + ret.addElement(old_mbrship.elementAt(i)); + return ret; + } + + + /** If hb_sender is not a member, send a SUSPECT to sender (after n pings received) */ + boolean checkPingerValidity(Object hb_sender) { + int num_pings=0; + Message shun_msg; + Header hdr; + + if(hb_sender != null && members != null && !members.contains(hb_sender)) { + if(invalid_pingers.containsKey(hb_sender)) { + num_pings=((Integer) invalid_pingers.get(hb_sender)).intValue(); + if(num_pings >= max_tries) { + if(log.isErrorEnabled()) log.error("sender " + hb_sender + + " is not member in " + members + " ! Telling it to leave group"); + shun_msg=new Message((Address) hb_sender, null, null); + hdr=new FdHeader(FdHeader.NOT_MEMBER); + shun_msg.putHeader(getName(), hdr); + passDown(new Event(Event.MSG, shun_msg)); + invalid_pingers.remove(hb_sender); + } + else { + num_pings++; + invalid_pingers.put(hb_sender, new Integer(num_pings)); + } + } + else { + num_pings++; + invalid_pingers.put(hb_sender, new Integer(num_pings)); + } + return false; + } + else + return true; + } + + + /* ----------------------------- End of Private Methods --------------------------- */ + + + + + + + public static class FdHeader extends Header implements Streamable { + static final byte HEARTBEAT=1; // sent periodically to a random member + static final byte NOT_MEMBER=2; // sent to the sender, when it is not a member anymore (shunned) + + + byte type=HEARTBEAT; + Address[] members=null; + long[] counters=null; // correlates with 'members' (same indexes) + + + public FdHeader() { + } // used for externalization + + FdHeader(byte type) { + this.type=type; + } + + FdHeader(byte type, int num_elements) { + this(type); + members=new Address[num_elements]; + counters=new long[num_elements]; + } + + + public String toString() { + switch(type) { + case HEARTBEAT: + return "[FD_PROB: HEARTBEAT]"; + case NOT_MEMBER: + return "[FD_PROB: NOT_MEMBER]"; + default: + return "[FD_PROB: unknown type (" + type + ")]"; + } + } + + public String printDetails() { + StringBuilder sb=new StringBuilder(); + Address mbr; + + if(members != null && counters != null) + for(int i=0; i < members.length; i++) { + mbr=members[i]; + if(mbr == null) + sb.append("\n"); + else + sb.append("\n" + mbr); + sb.append(": " + counters[i]); + } + return sb.toString(); + } + + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeByte(type); + + if(members != null) { + out.writeInt(members.length); + out.writeObject(members); + } + else + out.writeInt(0); + + if(counters != null) { + out.writeInt(counters.length); + for(int i=0; i < counters.length; i++) + out.writeLong(counters[i]); + } + else + out.writeInt(0); + } + + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + int num; + type=in.readByte(); + + num=in.readInt(); + if(num == 0) + members=null; + else { + members=(Address[]) in.readObject(); + } + + num=in.readInt(); + if(num == 0) + counters=null; + else { + counters=new long[num]; + for(int i=0; i < counters.length; i++) + counters[i]=in.readLong(); + } + } + + public long size() { + long retval=Global.BYTE_SIZE; + retval+=Global.SHORT_SIZE; // number of members + if(members != null && members.length > 0) { + for(int i=0; i < members.length; i++) { + Address member=members[i]; + retval+=Util.size(member); + } + } + + retval+=Global.SHORT_SIZE; // counters + if(counters != null && counters.length > 0) { + retval+=counters.length * Global.LONG_SIZE; + } + + return retval; + } + + public void writeTo(DataOutputStream out) throws IOException { + out.writeByte(type); + if(members == null || members.length == 0) + out.writeShort(0); + else { + out.writeShort(members.length); + for(int i=0; i < members.length; i++) { + Address member=members[i]; + Util.writeAddress(member, out); + } + } + + if(counters == null || counters.length == 0) { + out.writeShort(0); + } + else { + out.writeShort(counters.length); + for(int i=0; i < counters.length; i++) { + long counter=counters[i]; + out.writeLong(counter); + } + } + } + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + type=in.readByte(); + short len=in.readShort(); + if(len > 0) { + members=new Address[len]; + for(int i=0; i < len; i++) { + members[i]=Util.readAddress(in); + } + } + + len=in.readShort(); + if(len > 0) { + counters=new long[len]; + for(int i=0; i < counters.length; i++) { + counters[i]=in.readLong(); + } + } + } + + + } + + + private static class FdEntry { + private long counter=0; // heartbeat counter + private long timestamp=0; // last time the counter was incremented + private boolean excluded=false; // set to true if member was excluded from group + + + FdEntry() { + + } + + FdEntry(long counter) { + this.counter=counter; + timestamp=System.currentTimeMillis(); + } + + + long getCounter() { + return counter; + } + + long getTimestamp() { + return timestamp; + } + + boolean excluded() { + return excluded; + } + + + synchronized void setCounter(long new_counter) { + if(new_counter > counter) { // only set time if counter was incremented + timestamp=System.currentTimeMillis(); + counter=new_counter; + } + } + + synchronized void incrementCounter() { + counter++; + timestamp=System.currentTimeMillis(); + } + + synchronized void setTimestamp() { + timestamp=System.currentTimeMillis(); + } + + synchronized void setExcluded(boolean flag) { + excluded=flag; + } + + + public String toString() { + return "counter=" + counter + ", timestamp=" + timestamp + ", excluded=" + excluded; + } + + public String _toString() { + return "counter=" + counter + ", age=" + (System.currentTimeMillis() - timestamp) + + ", excluded=" + excluded; + } + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/obsolete/FD_RAND.java.txt =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/obsolete/FD_RAND.java.txt,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/obsolete/FD_RAND.java.txt 17 Aug 2012 14:51:27 -0000 1.1 @@ -0,0 +1,287 @@ +// $Id: FD_RAND.java.txt,v 1.1 2012/08/17 14:51:27 marcin Exp $ + +package org.jgroups.protocols; + +import java.util.Properties; +import java.util.Vector; + + +class FdRandHeader extends Header { + static final int HEARTBEAT = 0; + static final int HEARTBEAT_ACK = 1; + static final int SUSPECT = 2; + static final int REGULAR = 3; + + + int type=HEARTBEAT; + Object suspected_mbr=null; + + FdRandHeader(int type) {this.type=type;} + + + public String toString() { + switch(type) { + case HEARTBEAT: + return "[FD_RAND: heartbeat]"; + case HEARTBEAT_ACK: + return "[FD_RAND: heartbeat ack]"; + case SUSPECT: + return "[FD_RAND: suspect]"; + case REGULAR: + return "[FD_RAND: regular message]"; + default: + return "[FD_RAND: unknown type (" + type + ")]"; + } + } +} + + + + +/** + Failure detection based on simple heartbeat protocol. Regularly polls randomly + selected members for liveness. Passes SUSPECT message up the stack when a member is + not reachable. The simple algorithms works as follows: the membership is known. Each + HB protocol periodically sends a 'are-you-alive' message to a randomly selected + member, except itself. When a response hasn't been received for n milliseconds and m + tries, the corresponding member is suspected (and eventually excluded if faulty).

        + FD_RAND starts when it detects (in a view change notification) that there are at least + 2 members in the group. It stops running when the membership drops below 2. */ + +public class FD_RAND extends Protocol implements Runnable { + boolean trace=false; + Address ping_dest=null; + Address local_addr=null; + Thread pinger=null; + long timeout=1000; // 1 second between heartbeats + boolean ack_received=false; + Vector members=null; + int num_tries=0; + final int max_tries=2; // 3 tries before suspecting + Object ack_mutex=new Object(); + + + public String getName() {return "FD_RAND";} + + + public boolean setProperties(Properties props) {super.setProperties(props); + String str; + + this.props=props; + str=props.getProperty("trace"); + if(str != null) { + trace=new Boolean(str).booleanValue(); + props.remove("trace"); + } + + str=props.getProperty("timeout"); + if(str != null) { + timeout=new Long(str).longValue(); + props.remove("timeout"); + } + + + str=props.getProperty("num_tries"); + if(str != null) { + num_tries=new Integer(str).intValue(); + props.remove("num_tries"); + if(num_tries < 1) { + log.error("FD_RAND.setProperties(): propertiy 'num_tries' must be at least 1 ! " + + "Setting it to 1"); + num_tries=1; + } + } + + + if(props.size() > 0) { + log.error("FD_RAND.setProperties(): the following properties are not recognized: " + props); + + return false; + } + return true; + } + + + + + Address getPingDest() { + Address retval=null; + int r, size; + + if(members == null || members.size() < 2 || local_addr == null) + return null; + size=members.size(); + while(members.size() > 1) { + r=((int)(Math.random() * (size+1))) % size; + retval=(Address)members.elementAt(r); + if(local_addr.equals(retval)) + continue; + else + break; + } + return retval; + } + + + + public void up(Event evt) { + Message msg; + FdRandHeader hdr=null; + + switch(evt.getType()) { + + case Event.SET_LOCAL_ADDRESS: + local_addr=(Address)evt.getArg(); + break; + + case Event.MSG: + msg=(Message)evt.getArg(); + + try { + hdr=(FdRandHeader)msg.removeHeader(); + } + catch(Exception e) { + log.error("FD_RAND.up(): " + e); + } + + switch(hdr.type) { + case FdRandHeader.HEARTBEAT: // heartbeat request; send heartbeat ack + Message hb_ack=new Message(msg.getSrc(), null, null); + FdRandHeader tmp_hdr=new FdRandHeader(FdRandHeader.HEARTBEAT_ACK); + tmp_hdr.suspected_mbr=local_addr; + hb_ack.addHeader(tmp_hdr); + passDown(new Event(Event.MSG, hb_ack)); + return; // don't pass up ! + case FdRandHeader.HEARTBEAT_ACK: // heartbeat ack + Object suspect=hdr.suspected_mbr; + if(ping_dest != null && ping_dest.equals(suspect)) { + synchronized(ack_mutex) { + ack_received=true; + ack_mutex.notify(); + } + } + return; + case FdRandHeader.SUSPECT: + if(hdr.suspected_mbr != null) { + System.out.println("FD_RAND: SUSPECT(" + hdr.suspected_mbr + ")"); + passUp(new Event(Event.SUSPECT, hdr.suspected_mbr)); + } + return; + default: + break; + } + } + passUp(evt); // pass up to the layer above us + } + + + + public void down(Event evt) { + Message msg; + + switch(evt.getType()) { + case Event.STOP: + stop(); + passDown(evt); + break; + + case Event.VIEW_CHANGE: + synchronized(this) { + stop(); + View v=(View)evt.getArg(); + members=v != null ? v.getMembers() : null; + passDown(evt); + start(); + } + break; + + case Event.MSG: + msg=(Message)evt.getArg(); + msg.addHeader(new FdRandHeader(FdRandHeader.REGULAR)); // regular message + passDown(evt); + break; + + default: + passDown(evt); + break; + } + } + + + + /** + Loop while more than 1 member available. Choose a member randomly (not myself !) and send a + heartbeat. Wait for ack. If ack not received withing timeout, mcast SUSPECT message. + */ + public void run() { + Message suspect_msg, hb_req; + FdRandHeader hdr; + + + while(members.size() > 1 && pinger != null) { + ack_received=false; + num_tries=0; + + ping_dest=getPingDest(); + + while(!ack_received && num_tries <= max_tries && pinger != null) { + hb_req=new Message(ping_dest, null, null); + hb_req.addHeader(new FdRandHeader(FdRandHeader.HEARTBEAT)); // send heartbeat request + passDown(new Event(Event.MSG, hb_req)); + + synchronized(ack_mutex) { // wait for heartbeat ack + try {ack_mutex.wait(timeout);} + catch(Exception e) {} + } + if(pinger == null) return; + if(ack_received) { + Util.sleep(timeout); + break; + } + else { + if(num_tries >= max_tries) { + System.out.println("FD_RAND(" + local_addr + "): received no heartbeat ack from " + + ping_dest + ", suspecting it"); + hdr=new FdRandHeader(FdRandHeader.SUSPECT); + hdr.suspected_mbr=ping_dest; + suspect_msg=new Message(null, null, null); // mcast SUSPECT to all members + suspect_msg.addHeader(hdr); + passDown(new Event(Event.MSG, suspect_msg)); + break; + } + else { + num_tries++; + Util.sleep(timeout); + } + } + } + } + } + + + + + + void start() { + if(pinger == null) { + pinger=new Thread(this, "FD_RAND.PingerThread"); + pinger.start(); + } + } + + + void stop() { + Thread tmp=null; + num_tries=0; + ack_received=false; + if(pinger != null && pinger.isAlive()) { + tmp=pinger; + pinger=null; + tmp.interrupt(); + try {tmp.join(timeout);} catch(Exception ex) {} + } + pinger=null; + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/obsolete/FD_SHUN.java.txt =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/obsolete/FD_SHUN.java.txt,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/obsolete/FD_SHUN.java.txt 17 Aug 2012 14:51:27 -0000 1.1 @@ -0,0 +1,323 @@ +// $Id: FD_SHUN.java.txt,v 1.1 2012/08/17 14:51:27 marcin Exp $ + +package org.jgroups.protocols; + +import java.util.Hashtable; +import java.util.Properties; +import java.util.Vector; + + +class FdHeaderShun extends Header { + + static final int HEARTBEAT = 0; + static final int HEARTBEAT_ACK = 1; + static final int SUSPECT = 2; + static final int NOT_MEMBER = 3; // received as response by pinged mbr when we are not a member + + + + int type=HEARTBEAT; + Address suspected_mbr=null; + Address from=null; // member who detected that suspected_mbr has failed + + FdHeaderShun(int type) {this.type=type;} + + + public String toString() { + switch(type) { + case HEARTBEAT: + return "[FD_SHUN: heartbeat]"; + case HEARTBEAT_ACK: + return "[FD_SHUN: heartbeat ack]"; + case SUSPECT: + return "[FD_SHUN: SUSPECT (suspected_mbr=" + suspected_mbr + ", from=" + from + ")]"; + case NOT_MEMBER: return "[FD_SHUN: NOT_MEMBER]"; + default: + return "[FD_SHUN: unknown type (" + type + ")]"; + } + } +} + + + + +/** + Failure detection based on simple heartbeat protocol. Regularly polls members for + liveness. Passes SUSPECT message up the stack when a member is not reachable. The simple + algorithms works as follows: the membership is known and ordered. Each HB protocol + periodically sends a 'are-you-alive' message to its *neighbor*. A neighbor is the next in + rank in the membership list. It is recomputed upon a view change. When a response hasn't + been received for n milliseconds and m tries, the corresponding member is suspected (and + eventually excluded if faulty).

        + FD_SHUN starts when it detects (in a view change notification) that there are at least + 2 members in the group. It stops running when the membership drops below 2.

        + When a message is received from the monitored neighbor member, it causes the pinger thread to + 'skip' sending the next are-you-alive message. Thus, traffic is reduced.

        + When we receive a ping from a member that's not in the membership list, we shun it by sending it a + NOT_MEMBER message. That member will then leave the group (and possibly rejoin). +*/ + +public class FD_SHUN extends Protocol implements Runnable { + boolean trace=false; + Address ping_dest=null; + Address local_addr=null; + Thread pinger=null; // pinger thread + long timeout=3000; // number of millisecs to wait for an are-you-alive msg + boolean ack_received=false, skip_heartbeat=false; + int num_tries=0; + int max_tries=2; // number of times to send a are-you-alive msg (tot time= max_tries*timeout) + Vector members=null; + Hashtable invalid_pingers=new Hashtable(); // keys=Address, val=Integer (number of pings from suspected mbrs) + + + + public String getName() {return "FD_SHUN";} + + + public boolean setProperties(Properties props) {super.setProperties(props); + String str; + + this.props=props; + str=props.getProperty("trace"); + if(str != null) { + trace=new Boolean(str).booleanValue(); + props.remove("trace"); + } + + str=props.getProperty("timeout"); + if(str != null) { + timeout=new Long(str).longValue(); + props.remove("timeout"); + } + + str=props.getProperty("max_tries"); // before suspecting a member + if(str != null) { + max_tries=new Integer(str).intValue(); + props.remove("max_tries"); + } + if(props.size() > 0) { + log.error("FD_SHUN.setProperties(): the following properties are not recognized: " + props); + + return false; + } + return true; + } + + + + Address getPingDest(Vector members) { + Address tmp, retval=null; + + if(members == null || members.size() < 2 || local_addr == null) + return null; + for(int i=0; i < members.size(); i++) { + tmp=(Address)members.elementAt(i); + if(local_addr.equals(tmp)) { + if(i + 1 >= members.size()) + retval=(Address)members.elementAt(0); + else + retval=(Address)members.elementAt(i+1); + break; + } + } + return retval; + } + + + + public void up(Event evt) { + Message msg; + FdHeaderShun hdr=null; + Address sender; + Object tmphdr; + int num_pings=0; + + switch(evt.getType()) { + + case Event.SET_LOCAL_ADDRESS: + local_addr=(Address)evt.getArg(); + break; + + case Event.MSG: + msg=(Message)evt.getArg(); + tmphdr=msg.peekHeader(); + if(tmphdr == null || !(tmphdr instanceof FdHeaderShun)) { + if(ping_dest != null && (sender=msg.getSrc()) != null) { + if(ping_dest.equals(sender)) { + ack_received=true; + num_tries=0; + skip_heartbeat=true; + } + } + break; // message did not originate from FD_SHUN layer, just pass up + } + + hdr=(FdHeaderShun)msg.removeHeader(); + + switch(hdr.type) { + case FdHeaderShun.HEARTBEAT: // heartbeat request; send heartbeat ack + Address hb_sender=msg.getSrc(); + Message hb_ack=new Message(msg.getSrc(), null, null); + FdHeaderShun tmp_hdr=new FdHeaderShun(FdHeaderShun.HEARTBEAT_ACK); + + // 1. Send an ack + tmp_hdr.suspected_mbr=local_addr; + hb_ack.addHeader(tmp_hdr); + passDown(new Event(Event.MSG, hb_ack)); + + // 2. If sender is not a member, send a SUSPECT to sender (after n pings received) + if(hb_sender != null && members != null && !members.contains(hb_sender)) { + if(invalid_pingers.containsKey(hb_sender)) { + num_pings=((Integer)invalid_pingers.get(hb_sender)).intValue(); + if(num_pings >= max_tries) { + log.error("** FD_SHUN.up(HEARTBEAT): sender " + hb_sender + + " is not member in " + members + " ! Telling it to leave group"); + hb_ack=new Message(msg.getSrc(), null, null); + tmp_hdr=new FdHeaderShun(FdHeaderShun.NOT_MEMBER); + tmp_hdr.from=local_addr; + tmp_hdr.suspected_mbr=hb_sender; + hb_ack.addHeader(tmp_hdr); + passDown(new Event(Event.MSG, hb_ack)); + invalid_pingers.remove(hb_sender); + } + else { + num_pings++; + invalid_pingers.put(hb_sender, new Integer(num_pings)); + } + } + else { + num_pings++; + invalid_pingers.put(hb_sender, new Integer(num_pings)); + } + } + break; // don't pass up ! + + case FdHeaderShun.HEARTBEAT_ACK: // heartbeat ack + Object suspect=hdr.suspected_mbr; + if(ping_dest != null && ping_dest.equals(suspect)) { + ack_received=true; + num_tries=0; + } + else { + stop(); + ping_dest=(Address)getPingDest(members); + if(ping_dest != null) + start(); + } + break; + + case FdHeaderShun.SUSPECT: + if(hdr.suspected_mbr != null) { + if(trace) + System.out.println("FD_SHUN.up(SUSPECT): " + hdr); + passUp(new Event(Event.SUSPECT, hdr.suspected_mbr)); + passDown(new Event(Event.SUSPECT, hdr.suspected_mbr)); + } + break; + + case FdHeaderShun.NOT_MEMBER: + System.out.println("** FD_SHUN.up(NOT_MEMBER): I'm being shunned; exiting"); + passUp(new Event(Event.EXIT)); + break; + } + return; + } + passUp(evt); // pass up to the layer above us + } + + + + public void down(Event evt) { + Message msg; + + switch(evt.getType()) { + case Event.STOP: + stop(); + passDown(evt); + break; + + case Event.VIEW_CHANGE: + synchronized(this) { + stop(); + View v=(View)evt.getArg(); + members=(v != null) ? v.getMembers() : null; + passDown(evt); + ping_dest=(Address)getPingDest(members); + if(ping_dest != null) + start(); + } + break; + + default: + passDown(evt); + break; + } + } + + + + + public void run() { + Message suspect_msg, hb_req; + FdHeaderShun hdr; + + while(ping_dest != null && pinger != null) { + ack_received=false; + if(!skip_heartbeat) { + hb_req=new Message(ping_dest, null, null); + hb_req.addHeader(new FdHeaderShun(FdHeaderShun.HEARTBEAT)); // send heartbeat request + if(trace) System.out.println("FD_SHUN: sending are-you-alive msg to " + ping_dest); + passDown(new Event(Event.MSG, hb_req)); + } + skip_heartbeat=false; + Util.sleep(timeout); + if(pinger == null) + break; + + if(!ack_received && num_tries >= max_tries) { + if(trace) + System.out.println("FD_SHUN(" + local_addr + "): received no heartbeat ack from " + + ping_dest + ", suspecting it"); + hdr=new FdHeaderShun(FdHeaderShun.SUSPECT); + hdr.suspected_mbr=ping_dest; + hdr.from=local_addr; + suspect_msg=new Message(null, null, null); // mcast SUSPECT to all members + suspect_msg.addHeader(hdr); + passDown(new Event(Event.MSG, suspect_msg)); + members.removeElement(ping_dest); // try the next neighbor + ping_dest=(Address)getPingDest(members); + } + else { + if(trace && !skip_heartbeat) + System.out.println("FD_SHUN: received heartbeat ack from " + ping_dest); + num_tries++; + } + } + } + + + + + + void start() { + if(pinger == null) { + pinger=new Thread(this, "FD_SHUN.PingerThread"); + pinger.start(); + } + } + + void stop() { + Thread tmp=null; + if(pinger != null && pinger.isAlive()) { + tmp=pinger; + pinger=null; + tmp.interrupt(); + try {tmp.join(timeout);} catch(Exception ex) {} + if(tmp.isAlive()) + log.error("**** FD_SHUN.stop(): interrupted pinger thread is still alive !"); + } + pinger=null; + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/obsolete/PERF.java.txt =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/obsolete/PERF.java.txt,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/obsolete/PERF.java.txt 17 Aug 2012 14:51:27 -0000 1.1 @@ -0,0 +1,94 @@ +// $Id: PERF.java.txt,v 1.1 2012/08/17 14:51:27 marcin Exp $ + +package org.jgroups.protocols; + +import org.jgroups.Event; +import org.jgroups.Message; +import org.jgroups.View; +import org.jgroups.stack.Protocol; +import org.jgroups.stack.ProtocolObserver; + +import java.util.Properties; +import java.util.Vector; + + +/** + * Observes a protocol and adds its timings to the PerfHeader attached to each protocol. + */ +class PerfObserver implements ProtocolObserver { + final String prot_name; + boolean bottom=false; + + PerfObserver(String prot_name) { + this.prot_name=prot_name; + } + + + public void setProtocol(Protocol prot) { + if(prot != null && prot.getDownProtocol() == null) + bottom=true; + } + + + public boolean up(Event evt) { + PerfHeader hdr; + if(evt.getType() == Event.MSG) { + hdr=getPerfHeader((Message)evt.getArg()); + if(hdr != null) { + hdr.setReceived(prot_name, PerfHeader.UP); + if(bottom) + hdr.setNetworkReceived(); + } + } + return true; + } + + + public boolean passUp(Event evt) { + PerfHeader hdr; + if(evt.getType() == Event.MSG) { + hdr=getPerfHeader((Message)evt.getArg()); + if(hdr != null) { + hdr.setDone(prot_name, PerfHeader.UP); + } + } + return true; + } + + + public boolean down(Event evt) { + PerfHeader hdr; + if(evt.getType() == Event.MSG) { + hdr=getPerfHeader((Message)evt.getArg()); + if(hdr != null) { + hdr.setReceived(prot_name, PerfHeader.DOWN); + } + } + return true; + } + + + public boolean passDown(Event evt) { + PerfHeader hdr; + if(evt.getType() == Event.MSG) { + hdr=getPerfHeader((Message)evt.getArg()); + if(hdr != null) { + hdr.setDone(prot_name, PerfHeader.DOWN); + if(bottom) + hdr.setNetworkSent(); + } + } + return true; + } + + + PerfHeader getPerfHeader(Message m) { + Object hdr=null; + + if(m == null || (hdr=m.getHeader(PERF.name)) == null) + return null; + return (PerfHeader)hdr; + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/obsolete/PerfHeader.java.txt =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/obsolete/PerfHeader.java.txt,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/obsolete/PerfHeader.java.txt 17 Aug 2012 14:51:27 -0000 1.1 @@ -0,0 +1,433 @@ +// $Id: PerfHeader.java.txt,v 1.1 2012/08/17 14:51:27 marcin Exp $ + +package org.jgroups.protocols; + +import org.jgroups.Header; +import org.jgroups.Message; +import org.jgroups.stack.Protocol; +import org.jgroups.util.Util; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.*; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Vector; + + +/** + * Inserted by PERF into each message. Records the time taken by each protocol to process the message to + * which this header is attached. Travels down through the stack and up the other stack with the message. + * + * @author Bela Ban + */ +public class PerfHeader extends Header { + Object sender=null; + Object receiver=null; + long start_time=0; // time when header was created + long end_time=0; // time when header was received + long network_send=0; // time the packet was put on the network + long network_recv=0; // time the packet was received from the network + long network_time=0; // time spent on the network (between bottom layers) + HashMap down=new HashMap(); // key=protocol name, val=PerfEntry + HashMap up=new HashMap(); // key=protocol name, val=PerfEntry + final static int UP=1; + final static int DOWN=2; + final static String classname="org.jgroups.protocols.PerfHeader"; + static long size=0; + private static Message msg2; + static Log log=LogFactory.getLog(PerfHeader.class); + + + static { + size=Util.sizeOf(classname); + if(size <= 0) size=400; + } + + + // Needed for externalization + public PerfHeader() { + } + + + public PerfHeader(Object sender, Object receiver) { + this.sender=sender; + this.receiver=receiver; + start_time=System.currentTimeMillis(); + } + + + public String toString() { + return "[PerfHeader]"; + } + + + public String printContents(boolean detailed) { + return printContents(detailed, null); + } + + + public String printContents(boolean detailed, Vector prots) { + StringBuilder sb=new StringBuilder(); + String key; + PerfEntry val; + Protocol p; + + if(sender != null) + sb.append("sender=").append(sender).append('\n'); + if(receiver != null) + sb.append("receiver=").append(receiver).append('\n'); + + if(detailed) + sb.append("start_time=").append(start_time).append("\nend_time=").append(end_time).append('\n'); + + if(end_time >= start_time) + sb.append("total time=").append((end_time - start_time)).append('\n'); + else + sb.append("total time=n/a\n"); + + if(detailed) { + if(network_send > 0) sb.append("network_send=").append(network_send).append('\n'); + if(network_recv > 0) sb.append("network_recv=").append(network_recv).append('\n'); + } + + if(network_time > 0) + sb.append("network=").append(network_time).append('\n'); + + sb.append("\nDOWN\n-----\n"); + if(prots != null) { + for(int i=0; i < prots.size(); i++) { + p=(Protocol)prots.elementAt(i); + key=p.getName(); + val=(PerfEntry)down.get(key); + sb.append(key).append(':').append('\t').append(val.printContents(detailed)).append('\n'); + } + } + else + for(Iterator it=down.keySet().iterator(); it.hasNext();) { + key=(String)it.next(); + val=(PerfEntry)down.get(key); + sb.append(key).append(':').append('\t').append(val.printContents(detailed)).append('\n'); + } + + sb.append("\nUP\n-----\n"); + if(prots != null) { + for(int i=prots.size() - 1; i >= 0; i--) { + p=(Protocol)prots.elementAt(i); + key=p.getName(); + val=(PerfEntry)up.get(key); + sb.append(key).append(':').append('\t').append(val.printContents(detailed)).append('\n'); + } + } + else + for(Iterator it=up.keySet().iterator(); it.hasNext();) { + key=(String)it.next(); + val=(PerfEntry)up.get(key); + sb.append(key).append(':').append('\t').append(val.printContents(detailed)).append('\n'); + } + + + return sb.toString(); + } + + + public void setEndTime() { + end_time=System.currentTimeMillis(); + } + + + public void setReceived(String prot_name, int type) { + PerfEntry entry=getEntry(prot_name, type); + long t=System.currentTimeMillis(); + if(entry != null) + entry.setReceived(t); + } + + public void setDone(String prot_name, int type) { + PerfEntry entry=getEntry(prot_name, type); + long t=System.currentTimeMillis(); + if(entry != null) + entry.setDone(t); + } + + public void setNetworkSent() { + network_send=System.currentTimeMillis(); + } + + + public void setNetworkReceived() { + network_recv=System.currentTimeMillis(); + if(network_send > 0 && network_recv > network_send) + network_time=network_recv - network_send; + } + + + /** + * Adds a new entry to both hashtables + */ + public void addEntry(String prot_name) { + up.put(prot_name, new PerfEntry()); + down.put(prot_name, new PerfEntry()); + } + + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeObject(sender); + out.writeObject(receiver); + out.writeLong(start_time); + out.writeLong(end_time); + out.writeLong(network_send); + out.writeLong(network_recv); + out.writeLong(network_time); + writeHashtable(down, out); + writeHashtable(up, out); + } + + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + sender=in.readObject(); + receiver=in.readObject(); + start_time=in.readLong(); + end_time=in.readLong(); + network_send=in.readLong(); + network_recv=in.readLong(); + network_time=in.readLong(); + down=readHashtable(in); + up=readHashtable(in); + } + + + public long size() { + return size; + } + + + void writeHashtable(HashMap h, ObjectOutput out) { + String key; + PerfEntry val; + + try { + if(h == null) { + out.writeInt(0); + return; + } + out.writeInt(h.size()); + for(Iterator it=h.keySet().iterator(); it.hasNext();) { + key=(String)it.next(); + val=(PerfEntry)h.get(key); + if(key == null || val == null) { + System.err.println("PerfHeader.writeHashtable(): key or val is null"); + continue; + } + out.writeObject(key); + out.writeObject(val); + } + } + catch(Exception ex) { + System.err.println("PerfHeader.writeHashtable(): " + ex); + } + } + + + HashMap readHashtable(ObjectInput in) { + HashMap h=new HashMap(); + int num=0; + String key; + PerfEntry val; + + try { + num=in.readInt(); + if(num == 0) + return h; + for(int i=0; i < num; i++) { + key=(String)in.readObject(); + val=(PerfEntry)in.readObject(); + h.put(key, val); + } + } + catch(Exception ex) { + System.err.println("PerfHeader.readHashtable(): " + ex); + } + + return h; + } + + + PerfEntry getEntry(String prot_name, int type) { + HashMap tmp=null; + PerfEntry entry=null; + + if(prot_name == null) return null; + if(type == UP) + tmp=up; + else + if(type == DOWN) tmp=down; + if(tmp == null) return null; + entry=(PerfEntry)tmp.get(prot_name); + if(entry == null) + log.error("PerfHeader.getEntry(): protocol \"" + prot_name + "\" not found"); + return entry; + } + + + public static void main(String[] args) { + PerfHeader hdr=new PerfHeader(), hdr2; + Message msg; + ByteArrayOutputStream out_stream; + ByteArrayInputStream in_stream; + ObjectOutputStream out; + ObjectInputStream in; + byte[] out_buf, in_buf; + + + hdr.addEntry("GMS"); + hdr.addEntry("GMS"); + hdr.addEntry("FRAG"); + hdr.addEntry("FRAG"); + hdr.addEntry("UDP"); + hdr.addEntry("UDP"); + + + msg=new Message(); + msg.putHeader("PERF", hdr); + + + hdr.setReceived("GMS", PerfHeader.DOWN); + Util.sleep(2); + hdr.setDone("GMS", PerfHeader.DOWN); + + hdr.setReceived("FRAG", PerfHeader.DOWN); + Util.sleep(20); + hdr.setDone("FRAG", PerfHeader.DOWN); + + + long len=msg.size(); + System.out.println("Size is " + len); + + + hdr.setReceived("UDP", PerfHeader.DOWN); + Util.sleep(12); + hdr.setDone("UDP", PerfHeader.DOWN); + + + Util.sleep(30); + + hdr.setReceived("UDP", PerfHeader.UP); + hdr.setDone("UDP", PerfHeader.UP); + + hdr.setReceived("FRAG", PerfHeader.UP); + Util.sleep(23); + hdr.setDone("FRAG", PerfHeader.UP); + + hdr.setReceived("GMS", PerfHeader.UP); + Util.sleep(3); + hdr.setDone("GMS", PerfHeader.UP); + + + hdr.setEndTime(); + + System.out.println(hdr.printContents(true)); + + try { + System.out.println("Saving hdr to byte buffer"); + out_stream=new ByteArrayOutputStream(256); + out=new ObjectOutputStream(out_stream); + out.writeObject(msg); + out_buf=out_stream.toByteArray(); + + System.out.println("Constructing hdr2 from byte buffer"); + in_buf=out_buf; // ref + + in_stream=new ByteArrayInputStream(in_buf); + in=new ObjectInputStream(in_stream); + + msg2=(Message)in.readObject(); + hdr2=(PerfHeader)msg2.removeHeader("PERF"); + System.out.println(hdr2.printContents(true)); + } + catch(Exception ex) { + log.error(ex); + } + + + } + + +} + + +/** + * Entry specific for 1 protocol layer. Records time message was received by that layer and when message was passed on + */ +class PerfEntry implements Externalizable { + long received=0; + long done=0; + long total=-1; + + + // Needed for externalization + public PerfEntry() { + + } + + + public long getReceived() { + return received; + } + + public long getDone() { + return done; + } + + public long getTotal() { + return total; + } + + public void setReceived(long r) { + received=r; + } + + public void setDone(long d) { + done=d; + if(received > 0 && done > 0 && done >= received) + total=done - received; + } + + public String toString() { + if(total >= 0) + return "time: " + total; + else + return "time: n/a"; + } + + + public String printContents(boolean detailed) { + StringBuilder sb=new StringBuilder(); + if(detailed) { + if(received > 0) sb.append("received=").append(received); + if(done > 0) { + if(received > 0) sb.append(", "); + sb.append("done=").append(done); + } + } + if(detailed && (received > 0 || done > 0)) sb.append(", "); + sb.append(toString()); + return sb.toString(); + } + + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeLong(received); + out.writeLong(done); + out.writeLong(total); + } + + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + received=in.readLong(); + done=in.readLong(); + total=in.readLong(); + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/obsolete/TCP.java.txt =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/obsolete/TCP.java.txt,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/obsolete/TCP.java.txt 17 Aug 2012 14:51:27 -0000 1.1 @@ -0,0 +1,493 @@ +// $Id: TCP.java.txt,v 1.1 2012/08/17 14:51:27 marcin Exp $ + +package org.jgroups.protocols; + + +import org.jgroups.Address; +import org.jgroups.Event; +import org.jgroups.Message; +import org.jgroups.View; +import org.jgroups.blocks.ConnectionTable; +import org.jgroups.stack.IpAddress; +import org.jgroups.stack.Protocol; +import org.jgroups.util.BoundedList; +import org.jgroups.util.Util; + +import java.net.InetAddress; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.util.HashMap; +import java.util.Properties; +import java.util.Vector; + + + + +/** + * TCP based protocol. Creates a server socket, which gives us the local address of this group member. For + * each accept() on the server socket, a new thread is created that listens on the socket. + * For each outgoing message m, if m.dest is in the ougoing hashtable, the associated socket will be reused + * to send message, otherwise a new socket is created and put in the hashtable. + * When a socket connection breaks or a member is removed from the group, the corresponding items in the + * incoming and outgoing hashtables will be removed as well.
        + * This functionality is in ConnectionTable, which isT used by TCP. TCP sends messages using ct.send() and + * registers with the connection table to receive all incoming messages. + * @author Bela Ban + */ +public class TCP extends Protocol implements ConnectionTable.Receiver { + private ConnectionTable ct=null; + protected Address local_addr=null; + private String group_addr=null; + private InetAddress bind_addr=null; // local IP address to bind srv sock to (m-homed systems) + private InetAddress external_addr=null; // the IP address which is broadcast to other group members + private int start_port=7800; // find first available port starting at this port + private int end_port=0; // maximum port to bind to + private final Vector members=new Vector(11); + private long reaper_interval=0; // time in msecs between connection reaps + private long conn_expire_time=0; // max time a conn can be idle before being reaped + boolean loopback=false; // loops back msgs to self if true + + /** If set it will be added to local_addr. Used to implement + * for example transport independent addresses */ + byte[] additional_data=null; + + /** List the maintains the currently suspected members. This is used so we don't send too many SUSPECT + * events up the stack (one per message !) + */ + final BoundedList suspected_mbrs=new BoundedList(20); + + /** Should we drop unicast messages to suspected members or not */ + boolean skip_suspected_members=true; + + int recv_buf_size=150000; + int send_buf_size=150000; + int sock_conn_timeout=2000; // max time in millis for a socket creation in ConnectionTable + + static final String name="TCP"; + static final String IGNORE_BIND_ADDRESS_PROPERTY="ignore.bind.address"; + + int num_msgs_sent=0, num_msgs_received=0; + + + public TCP() { + } + + public String toString() { + return "Protocol TCP(local address: " + local_addr + ')'; + } + + public String getName() { + return "TCP"; + } + + public long getNumMessagesSent() {return num_msgs_sent;} + public long getNumMessagesReceived() {return num_msgs_received;} + public int getOpenConnections() {return ct.getNumConnections();} + public InetAddress getBindAddr() {return bind_addr;} + public void setBindAddr(InetAddress bind_addr) {this.bind_addr=bind_addr;} + public int getStartPort() {return start_port;} + public void setStartPort(int start_port) {this.start_port=start_port;} + public int getEndPort() {return end_port;} + public void setEndPort(int end_port) {this.end_port=end_port;} + public long getReaperInterval() {return reaper_interval;} + public void setReaperInterval(long reaper_interval) {this.reaper_interval=reaper_interval;} + public long getConnExpireTime() {return conn_expire_time;} + public void setConnExpireTime(long conn_expire_time) {this.conn_expire_time=conn_expire_time;} + public boolean isLoopback() {return loopback;} + public void setLoopback(boolean loopback) {this.loopback=loopback;} + public String printConnections() {return ct.toString();} + + + public void resetStats() { + super.resetStats(); + num_msgs_sent=num_msgs_received=0; + } + + protected final Vector getMembers() { + return members; + } + + /** + DON'T REMOVE ! This prevents the up-handler thread to be created, which essentially is superfluous: + messages are received from the network rather than from a layer below. + */ + public void startUpHandler() { + ; + } + + + public void start() throws Exception { + ct=getConnectionTable(reaper_interval,conn_expire_time,bind_addr,external_addr,start_port,end_port); + // ct.addConnectionListener(this); + ct.setReceiveBufferSize(recv_buf_size); + ct.setSendBufferSize(send_buf_size); + ct.setSocketConnectionTimeout(sock_conn_timeout); + local_addr=ct.getLocalAddress(); + if(additional_data != null && local_addr instanceof IpAddress) + ((IpAddress)local_addr).setAdditionalData(additional_data); + passUp(new Event(Event.SET_LOCAL_ADDRESS, local_addr)); + } + + /** + * @param reaperInterval + * @param connExpireTime + * @param bindAddress + * @param startPort + * @throws Exception + * @return ConnectionTable + * Sub classes overrides this method to initialize a different version of + * ConnectionTable. + */ + protected ConnectionTable getConnectionTable(long reaperInterval, long connExpireTime, InetAddress bindAddress, + InetAddress externalAddress, int startPort, int endPort) throws Exception { + ConnectionTable cTable=null; + if(reaperInterval == 0 && connExpireTime == 0) { + cTable=new ConnectionTable(this, bindAddress, externalAddress, startPort, endPort); + } + else { + if(reaperInterval == 0) { + reaperInterval=5000; + if(log.isWarnEnabled()) log.warn("reaper_interval was 0, set it to " + reaperInterval); + } + if(connExpireTime == 0) { + connExpireTime=1000 * 60 * 5; + if(log.isWarnEnabled()) log.warn("conn_expire_time was 0, set it to " + connExpireTime); + } + cTable=new ConnectionTable(this, bindAddress, externalAddress, startPort, endPort, + reaperInterval, connExpireTime); + } + return cTable; + } + + public void stop() { + ct.stop(); + } + + + /** + Sent to destination(s) using the ConnectionTable class. + */ + public void down(Event evt) { + Message msg; + Object dest_addr; + + if(evt.getType() != Event.MSG) { + handleDownEvent(evt); + return; + } + + msg=(Message)evt.getArg(); + num_msgs_sent++; + + if(group_addr != null) { // added patch sent by Roland Kurmann (bela March 20 2003) + /* Add header (includes channel name) */ + msg.putHeader(name, new TcpHeader(group_addr)); + } + + dest_addr=msg.getDest(); + + + /* Because we don't call Protocol.passDown(), we notify the observer directly (e.g. PerfObserver). This way, + we still have performance numbers for TCP */ + if(observer != null) + observer.passDown(evt); + + if(dest_addr == null) { // broadcast (to all members) + if(group_addr == null) { + if(log.isWarnEnabled()) log.warn("dest address of message is null, and " + + "sending to default address fails as group_addr is null, too !" + + " Discarding message."); + return; + } + else { + sendMulticastMessage(msg); // send to current membership + } + } + else { + sendUnicastMessage(msg); // send to a single member + } + } + + + /** ConnectionTable.Receiver interface */ + public void receive(Message msg) { + TcpHeader hdr=null; + Event evt=new Event(Event.MSG, msg); + + + /* Because Protocol.up() is never called by this bottommost layer, we call up() directly in the observer. + This allows e.g. PerfObserver to get the time of reception of a message */ + if(observer != null) + observer.up(evt, up_queue.size()); + + if(log.isTraceEnabled()) log.trace("received msg " + msg); + num_msgs_received++; + + hdr=(TcpHeader)msg.removeHeader(name); + + if(hdr != null) { + /* Discard all messages destined for a channel with a different name */ + String ch_name=null; + + if(hdr.group_addr != null) + ch_name=hdr.group_addr; + + // below lines were commented as patch sent by Roland Kurmann (bela March 20 2003) + +// if(group_addr == null) { +// if(log.isWarnEnabled()) log.warn("TCP.receive()", "group address in header was null, discarded"); +// return; +// } + + // Discard if message's group name is not the same as our group name unless the + // message is a diagnosis message (special group name DIAG_GROUP) + if(ch_name != null && !group_addr.equals(ch_name) && + !ch_name.equals(Util.DIAG_GROUP)) { + if(log.isWarnEnabled()) log.warn("discarded message from different group (" + + ch_name + "). Sender was " + msg.getSrc()); + return; + } + } + + passUp(evt); + } + + + // ConnectionTable.ConnectionListener interface +// public void connectionOpened(Address peer_addr) { +// if(log.isTraceEnabled()) log.trace("opened connection to " + peer_addr); +// } +// +// public void connectionClosed(Address peer_addr) { +// if(peer_addr != null) +// if(log.isTraceEnabled()) log.trace("closed connection to " + peer_addr); +// } + + + /** Setup the Protocol instance acording to the configuration string */ + public boolean setProperties(Properties props) { + String str, tmp=null; + + super.setProperties(props); + str=props.getProperty("start_port"); + if(str != null) { + start_port=Integer.parseInt(str); + props.remove("start_port"); + } + + str=props.getProperty("end_port"); + if(str != null) { + end_port=Integer.parseInt(str); + props.remove("end_port"); + } + + // PropertyPermission not granted if running in an untrusted environment with JNLP. + try { + tmp=System.getProperty("bind.address"); // set by JBoss + if(Boolean.getBoolean(IGNORE_BIND_ADDRESS_PROPERTY)) { + tmp=null; + } + } + catch (SecurityException ex){ + } + + if(tmp != null) + str=tmp; + else + str=props.getProperty("bind_addr"); + if(str != null) { + try { + bind_addr=InetAddress.getByName(str); + } + catch(UnknownHostException unknown) { + if(log.isFatalEnabled()) log.fatal("(bind_addr): host " + str + " not known"); + return false; + } + props.remove("bind_addr"); + } + + str=props.getProperty("external_addr"); + if(str != null) { + try { + external_addr=InetAddress.getByName(str); + } + catch(UnknownHostException unknown) { + if(log.isFatalEnabled()) log.fatal("(external_addr): host " + str + " not known"); + return false; + } + props.remove("external_addr"); + } + + str=props.getProperty("reaper_interval"); + if(str != null) { + reaper_interval=Long.parseLong(str); + props.remove("reaper_interval"); + } + + str=props.getProperty("conn_expire_time"); + if(str != null) { + conn_expire_time=Long.parseLong(str); + props.remove("conn_expire_time"); + } + + str=props.getProperty("sock_conn_timeout"); + if(str != null) { + sock_conn_timeout=Integer.parseInt(str); + props.remove("sock_conn_timeout"); + } + + str=props.getProperty("recv_buf_size"); + if(str != null) { + recv_buf_size=Integer.parseInt(str); + props.remove("recv_buf_size"); + } + + str=props.getProperty("send_buf_size"); + if(str != null) { + send_buf_size=Integer.parseInt(str); + props.remove("send_buf_size"); + } + + str=props.getProperty("loopback"); + if(str != null) { + loopback=Boolean.valueOf(str).booleanValue(); + props.remove("loopback"); + } + + str=props.getProperty("skip_suspected_members"); + if(str != null) { + skip_suspected_members=Boolean.valueOf(str).booleanValue(); + props.remove("skip_suspected_members"); + } + + if(props.size() > 0) { + log.error("TCP.setProperties(): the following properties are not recognized: " + props); + + return false; + } + return true; + } + + + /** + If the sender is null, set our own address. We cannot just go ahead and set the address + anyway, as we might be sending a message on behalf of someone else ! E.g. in case of + retransmission, when the original sender has crashed, or in a FLUSH protocol when we + have to return all unstable messages with the FLUSH_OK response. + */ + private void setSourceAddress(Message msg) { + if(msg.getSrc() == null) + msg.setSrc(local_addr); + } + + + /** Send a message to the address specified in msg.dest */ + private void sendUnicastMessage(Message msg) { + IpAddress dest; + Message copy; + Object hdr; + Event evt; + + dest=(IpAddress)msg.getDest(); // guaranteed not to be null + if(!(dest instanceof IpAddress)) { + if(log.isErrorEnabled()) log.error("destination address is not of type IpAddress !"); + return; + } + setSourceAddress(msg); + + /* Don't send if destination is local address. Instead, switch dst and src and put in up_queue */ + if(loopback && local_addr != null && dest != null && dest.equals(local_addr)) { + copy=msg.copy(); + hdr=copy.getHeader(name); + if(hdr != null && hdr instanceof TcpHeader) + copy.removeHeader(name); + copy.setSrc(local_addr); + copy.setDest(local_addr); + + evt=new Event(Event.MSG, copy); + + /* Because Protocol.up() is never called by this bottommost layer, we call up() directly in the observer. + This allows e.g. PerfObserver to get the time of reception of a message */ + if(observer != null) + observer.up(evt, up_queue.size()); + + passUp(evt); + return; + } + if(log.isTraceEnabled()) log.trace("dest=" + msg.getDest() + ", hdrs:\n" + msg.printObjectHeaders()); + try { + if(skip_suspected_members) { + if(suspected_mbrs.contains(dest)) { + if(log.isTraceEnabled()) log.trace("will not send unicast message to " + dest + + " as it is currently suspected"); + return; + } + } + ct.send(msg); + } + catch(SocketException e) { + if(members.contains(dest)) { + if(!suspected_mbrs.contains(dest)) { + suspected_mbrs.add(dest); + passUp(new Event(Event.SUSPECT, dest)); + } + } + } + } + + + protected void sendMulticastMessage(Message msg) { + Address dest; + Vector mbrs=(Vector)members.clone(); + for(int i=0; i < mbrs.size(); i++) { + dest=(Address)mbrs.elementAt(i); + msg.setDest(dest); + sendUnicastMessage(msg); + } + } + + + protected void handleDownEvent(Event evt) { + switch(evt.getType()) { + + case Event.TMP_VIEW: + case Event.VIEW_CHANGE: + suspected_mbrs.removeAll(); + synchronized(members) { + members.clear(); + members.addAll(((View)evt.getArg()).getMembers()); + } + break; + + case Event.GET_LOCAL_ADDRESS: // return local address -> Event(SET_LOCAL_ADDRESS, local) + passUp(new Event(Event.SET_LOCAL_ADDRESS, local_addr)); + break; + + case Event.CONNECT: + group_addr=(String)evt.getArg(); + + // removed March 18 2003 (bela), not needed (handled by GMS) + // Can't remove it; otherwise TCPGOSSIP breaks (bela May 8 2003) ! + passUp(new Event(Event.CONNECT_OK)); + break; + + case Event.DISCONNECT: + passUp(new Event(Event.DISCONNECT_OK)); + break; + + case Event.CONFIG: + if(log.isTraceEnabled()) log.trace("received CONFIG event: " + evt.getArg()); + handleConfigEvent((HashMap)evt.getArg()); + break; + } + } + + + void handleConfigEvent(HashMap map) { + if(map == null) return; + if(map.containsKey("additional_data")) + additional_data=(byte[])map.get("additional_data"); + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/obsolete/UDP.java.txt =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/obsolete/UDP.java.txt,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/obsolete/UDP.java.txt 17 Aug 2012 14:51:27 -0000 1.1 @@ -0,0 +1,1921 @@ +// $Id: UDP.java.txt,v 1.1 2012/08/17 14:51:27 marcin Exp $ + +package org.jgroups.protocols; + + +import org.jgroups.*; +import org.jgroups.stack.IpAddress; +import org.jgroups.stack.Protocol; +import org.jgroups.util.List; +import org.jgroups.util.*; +import org.jgroups.util.Queue; + +import java.io.*; +import java.net.*; +import java.util.*; + + + + +/** + * IP multicast transport based on UDP. Messages to the group (msg.dest == null) will + * be multicast (to all group members), whereas point-to-point messages + * (msg.dest != null) will be unicast to a single member. Uses a multicast and + * a unicast socket.

        + * The following properties are being read by the UDP protocol

        + * param mcast_addr - the multicast address to use default is 228.8.8.8
        + * param mcast_port - (int) the port that the multicast is sent on default is 7600
        + * param ip_mcast - (boolean) flag whether to use IP multicast - default is true
        + * param ip_ttl - Set the default time-to-live for multicast packets sent out on this + * socket. default is 32
        + * param use_packet_handler - If set, the mcast and ucast receiver threads just put + * the datagram's payload (a byte buffer) into a queue, from where a separate thread + * will dequeue and handle them (unmarshal and pass up). This frees the receiver + * threads from having to do message unmarshalling; this time can now be spent + * receiving packets. If you have lots of retransmissions because of network + * input buffer overflow, consider setting this property to true (default is false). + * @author Bela Ban + */ +public class UDP extends Protocol implements Runnable { + + /** Socket used for + *

          + *
        1. sending unicast packets and + *
        2. receiving unicast packets + *
        + * The address of this socket will be our local address (local_addr) */ + DatagramSocket sock=null; + + /** + * BoundedList of the last 100 ports used. This is to avoid reusing a port for DatagramSocket + */ + private static BoundedList last_ports_used=null; + + /** Maintain a list of local ports opened by DatagramSocket. If this is 0, this option is turned off. + * If bind_port is null, then this options will be ignored */ + int num_last_ports=100; + + /** IP multicast socket for receiving multicast packets */ + MulticastSocket mcast_recv_sock=null; + + /** IP multicast socket for sending multicast packets */ + MulticastSocket mcast_send_sock=null; + + /** + * Traffic class for sending unicast and multicast datagrams. + * Valid values are (check {@link #DatagramSocket.setTrafficClass(int)} for details): + *
          + *
        • IPTOS_LOWCOST (0x02), decimal 2
        • + *
        • IPTOS_RELIABILITY (0x04)<, decimal 4/LI> + *
        • IPTOS_THROUGHPUT (0x08), decimal 8
        • + *
        • IPTOS_LOWDELAY (0x10), decimal 16
        • + *
        + */ + int tos=0; // valid values: 2, 4, 8, 16 + + /** The address (host and port) of this member */ + IpAddress local_addr=null; + + /** The name of the group to which this member is connected */ + String channel_name=null; + + UdpHeader udp_hdr=null; + + /** The multicast address (mcast address and port) this member uses */ + IpAddress mcast_addr=null; + + /** The interface (NIC) to which the unicast and multicast sockets bind */ + InetAddress bind_addr=null; + + /** Bind the receiver multicast socket to all available interfaces (requires JDK 1.4) */ + boolean bind_to_all_interfaces=false; + + /** The port to which the unicast receiver socket binds. + * 0 means to bind to any (ephemeral) port */ + int bind_port=0; + int port_range=1; // 27-6-2003 bgooren, Only try one port by default + + /** The multicast address used for sending and receiving packets */ + String mcast_addr_name="228.8.8.8"; + + /** The multicast port used for sending and receiving packets */ + int mcast_port=7600; + + /** The multicast receiver thread */ + Thread mcast_receiver=null; + + /** The unicast receiver thread */ + UcastReceiver ucast_receiver=null; + + /** Whether to enable IP multicasting. If false, multiple unicast datagram + * packets are sent rather than one multicast packet */ + boolean ip_mcast=true; + + /** The time-to-live (TTL) for multicast datagram packets */ + int ip_ttl=64; + + /** The members of this group (updated when a member joins or leaves) */ + final Vector members=new Vector(11); + + /** Pre-allocated byte stream. Used for serializing datagram packets. Will grow as needed */ + final ExposedByteArrayOutputStream out_stream=new ExposedByteArrayOutputStream(1024); + + /** Send buffer size of the multicast datagram socket */ + int mcast_send_buf_size=32000; + + /** Receive buffer size of the multicast datagram socket */ + int mcast_recv_buf_size=64000; + + /** Send buffer size of the unicast datagram socket */ + int ucast_send_buf_size=32000; + + /** Receive buffer size of the unicast datagram socket */ + int ucast_recv_buf_size=64000; + + /** If true, messages sent to self are treated specially: unicast messages are + * looped back immediately, multicast messages get a local copy first and - + * when the real copy arrives - it will be discarded. Useful for Window + * media (non)sense */ + boolean loopback=true; + + + /** Discard packets with a different version. Usually minor version differences are okay. Setting this property + * to true means that we expect the exact same version on all incoming packets */ + boolean discard_incompatible_packets=false; + + /** Sometimes receivers are overloaded (they have to handle de-serialization etc). + * Packet handler is a separate thread taking care of de-serialization, receiver + * thread(s) simply put packet in queue and return immediately. Setting this to + * true adds one more thread */ + boolean use_incoming_packet_handler=false; + + /** Used by packet handler to store incoming DatagramPackets */ + Queue incoming_queue=null; + + /** Dequeues DatagramPackets from packet_queue, unmarshalls them and + * calls handleIncomingUdpPacket() */ + IncomingPacketHandler incoming_packet_handler=null; + + /** Packets to be sent are stored in outgoing_queue and sent by a separate thread. Enabling this + * value uses an additional thread */ + boolean use_outgoing_packet_handler=false; + + /** Used by packet handler to store outgoing DatagramPackets */ + Queue outgoing_queue=null; + + OutgoingPacketHandler outgoing_packet_handler=null; + + /** If set it will be added to local_addr. Used to implement + * for example transport independent addresses */ + byte[] additional_data=null; + + /** Maximum number of bytes for messages to be queued until they are sent. This value needs to be smaller + than the largest UDP datagram packet size */ + int max_bundle_size=AUTOCONF.senseMaxFragSizeStatic(); + + /** Max number of milliseconds until queued messages are sent. Messages are sent when max_bundle_size or + * max_bundle_timeout has been exceeded (whichever occurs faster) + */ + long max_bundle_timeout=20; + + /** Enabled bundling of smaller messages into bigger ones */ + boolean enable_bundling=false; + + /** Used by BundlingOutgoingPacketHandler */ + TimeScheduler timer=null; + + /** HashMap. Keys=senders, values=destinations. For each incoming message M with sender S, adds + * an entry with key=S and value= sender's IP address and port. + */ + HashMap addr_translation_table=new HashMap(); + + boolean use_addr_translation=false; + + /** The name of this protocol */ + static final String name="UDP"; + + static final String IGNORE_BIND_ADDRESS_PROPERTY="ignore.bind.address"; + + + /** Usually, src addresses are nulled, and the receiver simply sets them to the address of the sender. However, + * for multiple addresses on a Windows loopback device, this doesn't work + * (see http://jira.jboss.com/jira/browse/JGRP-79 and the JGroups wiki for details). This must be the same + * value for all members of the same group. Default is true, for performance reasons */ + boolean null_src_addresses=true; + + long num_msgs_sent=0, num_msgs_received=0, num_bytes_sent=0, num_bytes_received=0; + + + + /** + * public constructor. creates the UDP protocol, and initializes the + * state variables, does however not start any sockets or threads + */ + public UDP() { + ; + } + + /** + * debug only + */ + public String toString() { + return "UDP(local address: " + local_addr + ')'; + } + + public void resetStats() { + num_msgs_sent=num_msgs_received=num_bytes_sent=num_bytes_received=0; + } + + private BoundedList getLastPortsUsed() { + if(last_ports_used == null) + last_ports_used=new BoundedList(num_last_ports); + return last_ports_used; + } + + public long getNumMessagesSent() {return num_msgs_sent;} + public long getNumMessagesReceived() {return num_msgs_received;} + public long getNumBytesSent() {return num_bytes_sent;} + public long getNumBytesReceived() {return num_bytes_received;} + public String getBindAddress() {return bind_addr != null? bind_addr.toString() : "null";} + public void setBindAddress(String bind_addr) throws UnknownHostException { + this.bind_addr=InetAddress.getByName(bind_addr); + } + public boolean getBindToAllInterfaces() {return bind_to_all_interfaces;} + public void setBindToAllInterfaces(boolean flag) {this.bind_to_all_interfaces=flag;} + public boolean isDiscardIncompatiblePackets() {return discard_incompatible_packets;} + public void setDiscardIncompatiblePackets(boolean flag) {discard_incompatible_packets=flag;} + public boolean isEnableBundling() {return enable_bundling;} + public void setEnableBundling(boolean flag) {enable_bundling=flag;} + public int getMaxBundleSize() {return max_bundle_size;} + public void setMaxBundleSize(int size) {max_bundle_size=size;} + public long getMaxBundleTimeout() {return max_bundle_timeout;} + public void setMaxBundleTimeout(long timeout) {max_bundle_timeout=timeout;} + + /* ----------------------- Receiving of MCAST UDP packets ------------------------ */ + + public void run() { + DatagramPacket packet; + byte receive_buf[]=new byte[65535]; + int len, sender_port; + byte[] tmp, data; + InetAddress sender_addr; + + // moved out of loop to avoid excessive object creations (bela March 8 2001) + packet=new DatagramPacket(receive_buf, receive_buf.length); + + while(mcast_receiver != null && mcast_recv_sock != null) { + try { + packet.setData(receive_buf, 0, receive_buf.length); + mcast_recv_sock.receive(packet); + sender_addr=packet.getAddress(); + sender_port=packet.getPort(); + len=packet.getLength(); + data=packet.getData(); + + if(len == 4) { // received a diagnostics probe + if(data[0] == 'd' && data[1] == 'i' && data[2] == 'a' && data[3] == 'g') { + handleDiagnosticProbe(sender_addr, sender_port); + continue; + } + } + + if(log.isTraceEnabled()){ + StringBuilder sb=new StringBuilder("received (mcast) "); + sb.append(len).append(" bytes from ").append(sender_addr).append(':'); + sb.append(sender_port).append(" (size=").append(len).append(" bytes)"); + log.trace(sb.toString()); + } + if(len > receive_buf.length) { + if(log.isErrorEnabled()) log.error("size of the received packet (" + len + ") is bigger than " + + "allocated buffer (" + receive_buf.length + "): will not be able to handle packet. " + + "Use the FRAG protocol and make its frag_size lower than " + receive_buf.length); + } + + if(use_incoming_packet_handler) { + tmp=new byte[len]; + System.arraycopy(data, 0, tmp, 0, len); + incoming_queue.add(new IncomingQueueEntry(mcast_addr, sender_addr, sender_port, tmp)); + } + else + handleIncomingUdpPacket(mcast_addr, sender_addr, sender_port, data); + } + catch(SocketException sock_ex) { + if(log.isTraceEnabled()) log.trace("multicast socket is closed, exception=" + sock_ex); + break; + } + catch(InterruptedIOException io_ex) { // thread was interrupted + ; // go back to top of loop, where we will terminate loop + } + catch(Throwable ex) { + if(log.isErrorEnabled()) + log.error("failure in multicast receive()", ex); + Util.sleep(100); // so we don't get into 100% cpu spinning (should NEVER happen !) + } + } + if(log.isDebugEnabled()) log.debug("multicast thread terminated"); + } + + + private void handleDiagnosticProbe(InetAddress sender, int port) { + try { + byte[] diag_rsp=getDiagResponse().getBytes(); + DatagramPacket rsp=new DatagramPacket(diag_rsp, 0, diag_rsp.length, sender, port); + if(log.isDebugEnabled()) log.debug("sending diag response to " + sender + ':' + port); + sock.send(rsp); + } + catch(Throwable t) { + if(log.isErrorEnabled()) log.error("failed sending diag rsp to " + sender + ':' + port + + ", exception=" + t); + } + } + + private String getDiagResponse() { + StringBuilder sb=new StringBuilder(); + sb.append(local_addr).append(" (").append(channel_name).append(')'); + sb.append(" [").append(mcast_addr_name).append(':').append(mcast_port).append("]\n"); + sb.append("Version=").append(Version.description).append(", cvs=\"").append(Version.cvs).append("\"\n"); + sb.append("bound to ").append(bind_addr).append(':').append(bind_port).append('\n'); + sb.append("members: ").append(members).append('\n'); + + return sb.toString(); + } + + /* ------------------------------------------------------------------------------- */ + + + + /*------------------------------ Protocol interface ------------------------------ */ + + public String getName() { + return name; + } + + + public void init() throws Exception { + if(use_incoming_packet_handler) { + incoming_queue=new Queue(); + incoming_packet_handler=new IncomingPacketHandler(); + } + if(use_outgoing_packet_handler) { + outgoing_queue=new Queue(); + if(enable_bundling) { + timer=stack != null? stack.timer : null; + if(timer == null) + throw new Exception("timer could not be retrieved"); + outgoing_packet_handler=new BundlingOutgoingPacketHandler(); + } + else + outgoing_packet_handler=new OutgoingPacketHandler(); + } + } + + + /** + * Creates the unicast and multicast sockets and starts the unicast and multicast receiver threads + */ + public void start() throws Exception { + if(log.isDebugEnabled()) log.debug("creating sockets and starting threads"); + createSockets(); + passUp(new Event(Event.SET_LOCAL_ADDRESS, local_addr)); + startThreads(); + } + + + public void stop() { + if(log.isDebugEnabled()) log.debug("closing sockets and stopping threads"); + stopThreads(); // will close sockets, closeSockets() is not really needed anymore, but... + closeSockets(); // ... we'll leave it in there for now (doesn't do anything if already closed) + } + + + + /** + * Setup the Protocol instance acording to the configuration string + * The following properties are being read by the UDP protocol + * param mcast_addr - the multicast address to use default is 228.8.8.8 + * param mcast_port - (int) the port that the multicast is sent on default is 7600 + * param ip_mcast - (boolean) flag whether to use IP multicast - default is true + * param ip_ttl - Set the default time-to-live for multicast packets sent out on this socket. default is 32 + * @return true if no other properties are left. + * false if the properties still have data in them, ie , + * properties are left over and not handled by the protocol stack + */ + public boolean setProperties(Properties props) { + String str; + String tmp = null; + + super.setProperties(props); + + // PropertyPermission not granted if running in an untrusted environment with JNLP. + try { + tmp=System.getProperty("bind.address"); + if(Boolean.getBoolean(IGNORE_BIND_ADDRESS_PROPERTY)) { + tmp=null; + } + } + catch (SecurityException ex){ + } + + if(tmp != null) + str=tmp; + else + str=props.getProperty("bind_addr"); + if(str != null) { + try { + bind_addr=InetAddress.getByName(str); + } + catch(UnknownHostException unknown) { + if(log.isFatalEnabled()) log.fatal("(bind_addr): host " + str + " not known"); + return false; + } + props.remove("bind_addr"); + } + + str=props.getProperty("bind_to_all_interfaces"); + if(str != null) { + bind_to_all_interfaces=new Boolean(str).booleanValue(); + props.remove("bind_to_all_interfaces"); + } + + str=props.getProperty("bind_port"); + if(str != null) { + bind_port=Integer.parseInt(str); + props.remove("bind_port"); + } + + str=props.getProperty("num_last_ports"); + if(str != null) { + num_last_ports=Integer.parseInt(str); + props.remove("num_last_ports"); + } + + str=props.getProperty("start_port"); + if(str != null) { + bind_port=Integer.parseInt(str); + props.remove("start_port"); + } + + str=props.getProperty("port_range"); + if(str != null) { + port_range=Integer.parseInt(str); + props.remove("port_range"); + } + + str=props.getProperty("mcast_addr"); + if(str != null) { + mcast_addr_name=str; + props.remove("mcast_addr"); + } + + str=props.getProperty("mcast_port"); + if(str != null) { + mcast_port=Integer.parseInt(str); + props.remove("mcast_port"); + } + + str=props.getProperty("ip_mcast"); + if(str != null) { + ip_mcast=Boolean.valueOf(str).booleanValue(); + props.remove("ip_mcast"); + } + + str=props.getProperty("ip_ttl"); + if(str != null) { + ip_ttl=Integer.parseInt(str); + props.remove("ip_ttl"); + } + + str=props.getProperty("tos"); + if(str != null) { + tos=Integer.parseInt(str); + props.remove("tos"); + } + + str=props.getProperty("mcast_send_buf_size"); + if(str != null) { + mcast_send_buf_size=Integer.parseInt(str); + props.remove("mcast_send_buf_size"); + } + + str=props.getProperty("mcast_recv_buf_size"); + if(str != null) { + mcast_recv_buf_size=Integer.parseInt(str); + props.remove("mcast_recv_buf_size"); + } + + str=props.getProperty("ucast_send_buf_size"); + if(str != null) { + ucast_send_buf_size=Integer.parseInt(str); + props.remove("ucast_send_buf_size"); + } + + str=props.getProperty("ucast_recv_buf_size"); + if(str != null) { + ucast_recv_buf_size=Integer.parseInt(str); + props.remove("ucast_recv_buf_size"); + } + + str=props.getProperty("loopback"); + if(str != null) { + loopback=Boolean.valueOf(str).booleanValue(); + props.remove("loopback"); + } + + str=props.getProperty("discard_incompatible_packets"); + if(str != null) { + discard_incompatible_packets=Boolean.valueOf(str).booleanValue(); + props.remove("discard_incompatible_packets"); + } + + // this is deprecated, just left for compatibility (use use_incoming_packet_handler) + str=props.getProperty("use_packet_handler"); + if(str != null) { + use_incoming_packet_handler=Boolean.valueOf(str).booleanValue(); + props.remove("use_packet_handler"); + if(log.isWarnEnabled()) log.warn("'use_packet_handler' is deprecated; use 'use_incoming_packet_handler' instead"); + } + + str=props.getProperty("use_incoming_packet_handler"); + if(str != null) { + use_incoming_packet_handler=Boolean.valueOf(str).booleanValue(); + props.remove("use_incoming_packet_handler"); + } + + str=props.getProperty("use_outgoing_packet_handler"); + if(str != null) { + use_outgoing_packet_handler=Boolean.valueOf(str).booleanValue(); + props.remove("use_outgoing_packet_handler"); + } + + str=props.getProperty("max_bundle_size"); + if(str != null) { + int bundle_size=Integer.parseInt(str); + if(bundle_size > max_bundle_size) { + if(log.isErrorEnabled()) log.error("max_bundle_size (" + bundle_size + + ") is greater than largest UDP fragmentation size (" + max_bundle_size + ')'); + return false; + } + if(bundle_size <= 0) { + if(log.isErrorEnabled()) log.error("max_bundle_size (" + bundle_size + ") is <= 0"); + return false; + } + max_bundle_size=bundle_size; + props.remove("max_bundle_size"); + } + + str=props.getProperty("max_bundle_timeout"); + if(str != null) { + max_bundle_timeout=Long.parseLong(str); + if(max_bundle_timeout <= 0) { + if(log.isErrorEnabled()) log.error("max_bundle_timeout of " + max_bundle_timeout + " is invalid"); + return false; + } + props.remove("max_bundle_timeout"); + } + + str=props.getProperty("enable_bundling"); + if(str != null) { + enable_bundling=Boolean.valueOf(str).booleanValue(); + props.remove("enable_bundling"); + } + + str=props.getProperty("use_addr_translation"); + if(str != null) { + use_addr_translation=Boolean.valueOf(str).booleanValue(); + props.remove("use_addr_translation"); + } + + str=props.getProperty("null_src_addresses"); + if(str != null) { + null_src_addresses=Boolean.valueOf(str).booleanValue(); + props.remove("null_src_addresses"); + } + + if(props.size() > 0) { + log.error("UDP.setProperties(): the following properties are not recognized: " + props); + + return false; + } + + if(enable_bundling) { + if(use_outgoing_packet_handler == false) + if(log.isWarnEnabled()) log.warn("enable_bundling is true; setting use_outgoing_packet_handler=true"); + use_outgoing_packet_handler=true; + } + + return true; + } + + + + /** + * DON'T REMOVE ! This prevents the up-handler thread to be created, which essentially is superfluous: + * messages are received from the network rather than from a layer below. + */ + public void startUpHandler() { + ; + } + + /** + * handle the UP event. + * @param evt - the event being send from the stack + */ + public void up(Event evt) { + + switch(evt.getType()) { + + case Event.CONFIG: + passUp(evt); + if(log.isDebugEnabled()) log.debug("received CONFIG event: " + evt.getArg()); + handleConfigEvent((HashMap)evt.getArg()); + return; + } + + passUp(evt); + } + + /** + * Caller by the layer above this layer. Usually we just put this Message + * into the send queue and let one or more worker threads handle it. A worker thread + * then removes the Message from the send queue, performs a conversion and adds the + * modified Message to the send queue of the layer below it, by calling Down). + */ + public void down(Event evt) { + Message msg; + Object dest_addr; + + if(evt.getType() != Event.MSG) { // unless it is a message handle it and respond + handleDownEvent(evt); + return; + } + + msg=(Message)evt.getArg(); + + if(channel_name != null) { + // added patch by Roland Kurmann (March 20 2003) + // msg.putHeader(name, new UdpHeader(channel_name)); + msg.putHeader(name, udp_hdr); + } + + dest_addr=msg.getDest(); + + // Because we don't call Protocol.passDown(), we notify the observer directly (e.g. PerfObserver). + // This way, we still have performance numbers for UDP + if(observer != null) + observer.passDown(evt); + + if(dest_addr == null) { // 'null' means send to all group members + if(ip_mcast) { + if(mcast_addr == null) { + if(log.isErrorEnabled()) log.error("dest address of message is null, and " + + "sending to default address fails as mcast_addr is null, too !" + + " Discarding message " + Util.printEvent(evt)); + return; + } + // if we want to use IP multicast, then set the destination of the message + msg.setDest(mcast_addr); + } + else { + //sends a separate UDP message to each address + sendMultipleUdpMessages(msg, members); + return; + } + } + + try { + sendUdpMessage(msg); + } + catch(Exception e) { + if(log.isErrorEnabled()) log.error("exception=" + e + ", msg=" + msg + ", mcast_addr=" + mcast_addr); + } + } + + + + + + + /*--------------------------- End of Protocol interface -------------------------- */ + + + /* ------------------------------ Private Methods -------------------------------- */ + + + + /** + * If the sender is null, set our own address. We cannot just go ahead and set the address + * anyway, as we might be sending a message on behalf of someone else ! E.gin case of + * retransmission, when the original sender has crashed, or in a FLUSH protocol when we + * have to return all unstable messages with the FLUSH_OK response. + */ + private void setSourceAddress(Message msg) { + if(msg.getSrc() == null) + msg.setSrc(local_addr); + } + + + + + /** + * Processes a packet read from either the multicast or unicast socket. Needs to be synchronized because + * mcast or unicast socket reads can be concurrent. + * Correction (bela April 19 2005): we acces no instance variables, all vars are allocated on the stack, so + * this method should be reentrant: removed 'synchronized' keyword + */ + void handleIncomingUdpPacket(IpAddress dest, InetAddress sender, int port, byte[] data) { + ByteArrayInputStream inp_stream=null; + DataInputStream inp=null; + Message msg=null; + List l; // used if bundling is enabled + short version; + boolean is_message_list; + + try { + // skip the first n bytes (default: 4), this is the version info + inp_stream=new ByteArrayInputStream(data); + inp=new DataInputStream(inp_stream); + version=inp.readShort(); + + if(Version.compareTo(version) == false) { + if(log.isWarnEnabled()) { + StringBuilder sb=new StringBuilder(); + sb.append("packet from ").append(sender).append(':').append(port); + sb.append(" has different version (").append(version); + sb.append(") from ours (").append(Version.printVersion()).append("). "); + if(discard_incompatible_packets) + sb.append("Packet is discarded"); + else + sb.append("This may cause problems"); + log.warn(sb.toString()); + } + if(discard_incompatible_packets) + return; + } + + is_message_list=inp.readBoolean(); + if(is_message_list) { + l=bufferToList(inp, dest); + for(Enumeration en=l.elements(); en.hasMoreElements();) { + msg=(Message)en.nextElement(); + try { + handleMessage(msg); + } + catch(Throwable t) { + if(log.isErrorEnabled()) + log.error("failed unmarshalling message list", t); + } + } + } + else { + msg=bufferToMessage(inp, dest, sender, port); + handleMessage(msg); + } + } + catch(Throwable e) { + if(log.isErrorEnabled()) log.error("exception in processing incoming packet", e); + } + finally { + Util.closeInputStream(inp); + Util.closeInputStream(inp_stream); + } + } + + + + void handleMessage(Message msg) { + Event evt; + UdpHeader hdr; + Address dst=msg.getDest(); + + if(dst == null) + dst=mcast_addr; + + if(stats) { + num_msgs_received++; + num_bytes_received+=msg.getLength(); + } + + // discard my own multicast loopback copy + if(loopback) { + Address src=msg.getSrc(); + if((dst == null || (dst != null && dst.isMulticastAddress())) && src != null && local_addr.equals(src)) { + if(log.isTraceEnabled()) + log.trace("discarded own loopback multicast packet"); + return; + } + } + + evt=new Event(Event.MSG, msg); + if(log.isTraceEnabled()) { + StringBuilder sb=new StringBuilder("message is "); + sb.append(msg).append(", headers are ").append(msg.getHeaders()); + log.trace(sb.toString()); + } + + /* Because Protocol.up() is never called by this bottommost layer, we call up() directly in the observer. + * This allows e.g. PerfObserver to get the time of reception of a message */ + if(observer != null) + observer.up(evt, up_queue.size()); + + hdr=(UdpHeader)msg.getHeader(name); // replaced removeHeader() with getHeader() + if(hdr != null) { + + /* Discard all messages destined for a channel with a different name */ + String ch_name=hdr.channel_name; + + // Discard if message's group name is not the same as our group name unless the + // message is a diagnosis message (special group name DIAG_GROUP) + if(ch_name != null && channel_name != null && !channel_name.equals(ch_name) && + !ch_name.equals(Util.DIAG_GROUP)) { + if(log.isWarnEnabled()) log.warn("discarded message from different group (" + + ch_name + "). Sender was " + msg.getSrc()); + return; + } + } + else { + if(log.isErrorEnabled()) log.error("message does not have a UDP header"); + } + passUp(evt); + } + + + void sendUdpMessage(Message msg) throws Exception { + sendUdpMessage(msg, false); + } + + /** Send a message to the address specified in dest */ + void sendUdpMessage(Message msg, boolean copyForOutgoingQueue) throws Exception { + IpAddress dest; + Message copy; + Event evt; + + dest=(IpAddress)msg.getDest(); // guaranteed to be non-null + setSourceAddress(msg); + + if(log.isTraceEnabled()) { + StringBuilder sb=new StringBuilder("sending msg to "); + sb.append(msg.getDest()).append(" (src=").append(msg.getSrc()).append("), headers are ").append(msg.getHeaders()); + log.trace(sb.toString()); + } + + // Don't send if destination is local address. Instead, switch dst and src and put in up_queue. + // If multicast message, loopback a copy directly to us (but still multicast). Once we receive this, + // we will discard our own multicast message + if(loopback && (dest.equals(local_addr) || dest.isMulticastAddress())) { + copy=msg.copy(); + // copy.removeHeader(name); // we don't remove the header + copy.setSrc(local_addr); + // copy.setDest(dest); + evt=new Event(Event.MSG, copy); + + /* Because Protocol.up() is never called by this bottommost layer, we call up() directly in the observer. + This allows e.g. PerfObserver to get the time of reception of a message */ + if(observer != null) + observer.up(evt, up_queue.size()); + if(log.isTraceEnabled()) log.trace("looped back local message " + copy); + passUp(evt); + if(dest != null && !dest.isMulticastAddress()) + return; + } + + if(use_outgoing_packet_handler) { + if(copyForOutgoingQueue) + outgoing_queue.add(msg.copy()); + else + outgoing_queue.add(msg); + return; + } + + send(msg); + } + + + /** Internal method to serialize and send a message. This method is not reentrant */ + void send(Message msg) throws Exception { + Buffer buf; + IpAddress dest=(IpAddress)msg.getDest(); // guaranteed to be non-null + IpAddress src=(IpAddress)msg.getSrc(); + + synchronized(out_stream) { + buf=messageToBuffer(msg, dest, src); + doSend(buf, dest.getIpAddress(), dest.getPort()); + } + } + + + + void doSend(Buffer buf, InetAddress dest, int port) throws IOException { + DatagramPacket packet; + + // packet=new DatagramPacket(data, data.length, dest, port); + packet=new DatagramPacket(buf.getBuf(), buf.getOffset(), buf.getLength(), dest, port); + if(stats) { + num_msgs_sent++; + num_bytes_sent+=buf.getLength(); + } + if(dest.isMulticastAddress() && mcast_send_sock != null) { // mcast_recv_sock might be null if ip_mcast is false + mcast_send_sock.send(packet); + } + else { + if(sock != null) + sock.send(packet); + } + } + + + + void sendMultipleUdpMessages(Message msg, Vector dests) { + Address dest; + + for(int i=0; i < dests.size(); i++) { + dest=(Address)dests.elementAt(i); + msg.setDest(dest); + + try { + sendUdpMessage(msg, + true); // copy for outgoing queue if outgoing queue handler is enabled + } + catch(Exception e) { + if(log.isErrorEnabled()) log.error("failed sending multiple messages", e); + } + } + } + + + /** + * This method needs to be synchronized on out_stream when it is called + * @param msg + * @param dest + * @param src + * @return + * @throws IOException + */ + private Buffer messageToBuffer(Message msg, IpAddress dest, IpAddress src) throws Exception { + Buffer retval; + DataOutputStream out=null; + + try { + out_stream.reset(); + out=new DataOutputStream(out_stream); + out.writeShort(Version.version); // write the version + out.writeBoolean(false); // single message, *not* a list of messages + nullAddresses(msg, dest, src); + msg.writeTo(out); + revertAddresses(msg, dest, src); + out.flush(); + retval=new Buffer(out_stream.getRawBuffer(), 0, out_stream.size()); + return retval; + } + finally { + Util.closeOutputStream(out); + } + } + + + private void nullAddresses(Message msg, IpAddress dest, IpAddress src) { + msg.setDest(null); + if(!dest.isMulticastAddress()) { // unicast + if(src != null) { + if(null_src_addresses) + msg.setSrc(new IpAddress(src.getPort(), false)); // null the host part, leave the port + if(src.getAdditionalData() != null) + ((IpAddress)msg.getSrc()).setAdditionalData(src.getAdditionalData()); + } + else { + msg.setSrc(null); + } + } + else { // multicast + if(src != null) { + if(null_src_addresses) + msg.setSrc(new IpAddress(src.getPort(), false)); // null the host part, leave the port + if(src.getAdditionalData() != null) + ((IpAddress)msg.getSrc()).setAdditionalData(src.getAdditionalData()); + } + } + } + + private void revertAddresses(Message msg, IpAddress dest, IpAddress src) { + msg.setDest(dest); + msg.setSrc(src); + } + + + private Message bufferToMessage(DataInputStream instream, IpAddress dest, InetAddress sender, int port) + throws Exception { + Message msg=new Message(); + msg.readFrom(instream); + setAddresses(msg, dest, sender, port); + return msg; + } + + + private void setAddresses(Message msg, IpAddress dest, InetAddress sender, int port) { + // set the destination address + if(msg.getDest() == null && dest != null) + msg.setDest(dest); + + // set the source address if not set + IpAddress src_addr=(IpAddress)msg.getSrc(); + if(src_addr == null) { + try {msg.setSrc(new IpAddress(sender, port));} catch(Throwable t) {} + } + else { + byte[] tmp_additional_data=src_addr.getAdditionalData(); + if(src_addr.getIpAddress() == null) { + try {msg.setSrc(new IpAddress(sender, src_addr.getPort()));} catch(Throwable t) {} + } + if(tmp_additional_data != null) + ((IpAddress)msg.getSrc()).setAdditionalData(tmp_additional_data); + } + } + + + + private Buffer listToBuffer(List l, IpAddress dest) throws Exception { + Buffer retval=null; + IpAddress src; + Message msg; + int len=l != null? l.size() : 0; + boolean src_written=false; + DataOutputStream out=null; + out_stream.reset(); + + try { + out=new DataOutputStream(out_stream); + out.writeShort(Version.version); + out.writeBoolean(true); + out.writeInt(len); + + for(Enumeration en=l.elements(); en.hasMoreElements();) { + msg=(Message)en.nextElement(); + src=(IpAddress)msg.getSrc(); + if(!src_written) { + Util.writeAddress(src, out); + src_written=true; + } + msg.setDest(null); + msg.setSrc(null); + msg.writeTo(out); + revertAddresses(msg, dest, src); + } + out.flush(); + retval=new Buffer(out_stream.getRawBuffer(), 0, out_stream.size()); + return retval; + } + finally { + Util.closeOutputStream(out); + } + } + + private List bufferToList(DataInputStream instream, IpAddress dest) throws Exception { + List l=new List(); + DataInputStream in=null; + int len; + Message msg; + Address src; + + try { + len=instream.readInt(); + src=Util.readAddress(instream); + for(int i=0; i < len; i++) { + msg=new Message(); + msg.readFrom(instream); + msg.setDest(dest); + msg.setSrc(src); + l.add(msg); + } + return l; + } + finally { + Util.closeInputStream(in); + } + } + + + + /** + * Create UDP sender and receiver sockets. Currently there are 2 sockets + * (sending and receiving). This is due to Linux's non-BSD compatibility + * in the JDK port (see DESIGN). + */ + void createSockets() throws Exception { + InetAddress tmp_addr=null; + + // bind_addr not set, try to assign one by default. This is needed on Windows + + // changed by bela Feb 12 2003: by default multicast sockets will be bound to all network interfaces + + // CHANGED *BACK* by bela March 13 2003: binding to all interfaces did not result in a correct + // local_addr. As a matter of fact, comparison between e.g. 0.0.0.0:1234 (on hostA) and + // 0.0.0.0:1.2.3.4 (on hostB) would fail ! + if(bind_addr == null) { + InetAddress[] interfaces=InetAddress.getAllByName(InetAddress.getLocalHost().getHostAddress()); + if(interfaces != null && interfaces.length > 0) + bind_addr=interfaces[0]; + } + if(bind_addr == null) + bind_addr=InetAddress.getLocalHost(); + + if(bind_addr != null) + if(log.isInfoEnabled()) log.info("sockets will use interface " + bind_addr.getHostAddress()); + + + // 2. Create socket for receiving unicast UDP packets. The address and port + // of this socket will be our local address (local_addr) + if(bind_port > 0) { + sock=createDatagramSocketWithBindPort(); + } + else { + sock=createEphemeralDatagramSocket(); + } + if(tos > 0) { + try { + sock.setTrafficClass(tos); + } + catch(SocketException e) { + log.warn("traffic class of " + tos + " could not be set, will be ignored", e); + } + } + + if(sock == null) + throw new Exception("UDP.createSocket(): sock is null"); + + local_addr=new IpAddress(sock.getLocalAddress(), sock.getLocalPort()); + if(additional_data != null) + local_addr.setAdditionalData(additional_data); + + + // 3. Create socket for receiving IP multicast packets + if(ip_mcast) { + // 3a. Create mcast receiver socket + mcast_recv_sock=new MulticastSocket(mcast_port); + mcast_recv_sock.setTimeToLive(ip_ttl); + tmp_addr=InetAddress.getByName(mcast_addr_name); + mcast_addr=new IpAddress(tmp_addr, mcast_port); + + if(bind_to_all_interfaces) { + bindToAllInterfaces(mcast_recv_sock, mcast_addr.getIpAddress()); + } + else { + if(bind_addr != null) + mcast_recv_sock.setInterface(bind_addr); + mcast_recv_sock.joinGroup(tmp_addr); + } + + // 3b. Create mcast sender socket + mcast_send_sock=new MulticastSocket(); + mcast_send_sock.setTimeToLive(ip_ttl); + if(bind_addr != null) + mcast_send_sock.setInterface(bind_addr); + + if(tos > 0) { + try { + mcast_send_sock.setTrafficClass(tos); // high throughput + } + catch(SocketException e) { + log.warn("traffic class of " + tos + " could not be set, will be ignored", e); + } + } + } + + setBufferSizes(); + if(log.isInfoEnabled()) log.info("socket information:\n" + dumpSocketInfo()); + } + + + private void bindToAllInterfaces(MulticastSocket s, InetAddress mcastAddr) throws IOException { + SocketAddress tmp_mcast_addr=new InetSocketAddress(mcastAddr, mcast_port); + Enumeration en=NetworkInterface.getNetworkInterfaces(); + while(en.hasMoreElements()) { + NetworkInterface i=(NetworkInterface)en.nextElement(); + for(Enumeration en2=i.getInetAddresses(); en2.hasMoreElements();) { + InetAddress addr=(InetAddress)en2.nextElement(); + // if(addr.isLoopbackAddress()) + // continue; + s.joinGroup(tmp_mcast_addr, i); + if(log.isTraceEnabled()) + log.trace("joined " + tmp_mcast_addr + " on interface " + i.getName() + " (" + addr + ")"); + break; + } + } + } + + + /** Creates a DatagramSocket with a random port. Because in certain operating systems, ports are reused, + * we keep a list of the n last used ports, and avoid port reuse */ + private DatagramSocket createEphemeralDatagramSocket() throws SocketException { + DatagramSocket tmp=null; + int localPort=0; + while(true) { + tmp=new DatagramSocket(localPort, bind_addr); // first time localPort is 0 + if(num_last_ports <= 0) + break; + localPort=tmp.getLocalPort(); + if(getLastPortsUsed().contains(new Integer(localPort))) { + if(log.isDebugEnabled()) + log.debug("local port " + localPort + " already seen in this session; will try to get other port"); + try {tmp.close();} catch(Throwable e) {} + localPort++; + continue; + } + else { + getLastPortsUsed().add(new Integer(localPort)); + break; + } + } + return tmp; + } + + + + + /** + * Creates a DatagramSocket when bind_port > 0. Attempts to allocate the socket with port == bind_port, and + * increments until it finds a valid port, or until port_range has been exceeded + * @return DatagramSocket The newly created socket + * @throws Exception + */ + private DatagramSocket createDatagramSocketWithBindPort() throws Exception { + DatagramSocket tmp=null; + // 27-6-2003 bgooren, find available port in range (start_port, start_port+port_range) + int rcv_port=bind_port, max_port=bind_port + port_range; + while(rcv_port <= max_port) { + try { + tmp=new DatagramSocket(rcv_port, bind_addr); + break; + } + catch(SocketException bind_ex) { // Cannot listen on this port + rcv_port++; + } + catch(SecurityException sec_ex) { // Not allowed to listen on this port + rcv_port++; + } + + // Cannot listen at all, throw an Exception + if(rcv_port >= max_port + 1) { // +1 due to the increment above + throw new Exception("UDP.createSockets(): cannot list on any port in range " + + bind_port + '-' + (bind_port + port_range)); + } + } + return tmp; + } + + + private String dumpSocketInfo() throws Exception { + StringBuilder sb=new StringBuilder(128); + sb.append("local_addr=").append(local_addr); + sb.append(", mcast_addr=").append(mcast_addr); + sb.append(", bind_addr=").append(bind_addr); + sb.append(", ttl=").append(ip_ttl); + + if(sock != null) { + sb.append("\nsock: bound to "); + sb.append(sock.getLocalAddress().getHostAddress()).append(':').append(sock.getLocalPort()); + sb.append(", receive buffer size=").append(sock.getReceiveBufferSize()); + sb.append(", send buffer size=").append(sock.getSendBufferSize()); + } + + if(mcast_recv_sock != null) { + sb.append("\nmcast_recv_sock: bound to "); + sb.append(mcast_recv_sock.getInterface().getHostAddress()).append(':').append(mcast_recv_sock.getLocalPort()); + sb.append(", send buffer size=").append(mcast_recv_sock.getSendBufferSize()); + sb.append(", receive buffer size=").append(mcast_recv_sock.getReceiveBufferSize()); + } + + if(mcast_send_sock != null) { + sb.append("\nmcast_send_sock: bound to "); + sb.append(mcast_send_sock.getInterface().getHostAddress()).append(':').append(mcast_send_sock.getLocalPort()); + sb.append(", send buffer size=").append(mcast_send_sock.getSendBufferSize()); + sb.append(", receive buffer size=").append(mcast_send_sock.getReceiveBufferSize()); + } + return sb.toString(); + } + + + void setBufferSizes() { + if(sock != null) { + try { + sock.setSendBufferSize(ucast_send_buf_size); + } + catch(Throwable ex) { + if(log.isWarnEnabled()) log.warn("failed setting ucast_send_buf_size in sock: " + ex); + } + try { + sock.setReceiveBufferSize(ucast_recv_buf_size); + } + catch(Throwable ex) { + if(log.isWarnEnabled()) log.warn("failed setting ucast_recv_buf_size in sock: " + ex); + } + } + + if(mcast_recv_sock != null) { + try { + mcast_recv_sock.setSendBufferSize(mcast_send_buf_size); + } + catch(Throwable ex) { + if(log.isWarnEnabled()) log.warn("failed setting mcast_send_buf_size in mcast_recv_sock: " + ex); + } + + try { + mcast_recv_sock.setReceiveBufferSize(mcast_recv_buf_size); + } + catch(Throwable ex) { + if(log.isWarnEnabled()) log.warn("failed setting mcast_recv_buf_size in mcast_recv_sock: " + ex); + } + } + + if(mcast_send_sock != null) { + try { + mcast_send_sock.setSendBufferSize(mcast_send_buf_size); + } + catch(Throwable ex) { + if(log.isWarnEnabled()) log.warn("failed setting mcast_send_buf_size in mcast_send_sock: " + ex); + } + + try { + mcast_send_sock.setReceiveBufferSize(mcast_recv_buf_size); + } + catch(Throwable ex) { + if(log.isWarnEnabled()) log.warn("failed setting mcast_recv_buf_size in mcast_send_sock: " + ex); + } + } + + } + + + /** + * Closed UDP unicast and multicast sockets + */ + void closeSockets() { + // 1. Close multicast socket + closeMulticastSocket(); + + // 2. Close socket + closeSocket(); + } + + + void closeMulticastSocket() { + if(mcast_recv_sock != null) { + try { + if(mcast_addr != null) { + mcast_recv_sock.leaveGroup(mcast_addr.getIpAddress()); + } + mcast_recv_sock.close(); // this will cause the mcast receiver thread to break out of its loop + mcast_recv_sock=null; + if(log.isDebugEnabled()) log.debug("multicast receive socket closed"); + } + catch(IOException ex) { + } + mcast_addr=null; + } + + if(mcast_send_sock != null) { + mcast_send_sock.close(); // this will cause the mcast receiver thread to break out of its loop + mcast_send_sock=null; + if(log.isDebugEnabled()) log.debug("multicast send socket closed"); + } + } + + + void closeSocket() { + if(sock != null) { + sock.close(); + sock=null; + if(log.isDebugEnabled()) log.debug("socket closed"); + } + } + + + + + /** + * Starts the unicast and multicast receiver threads + */ + void startThreads() throws Exception { + if(ucast_receiver == null) { + //start the listener thread of the ucast_recv_sock + ucast_receiver=new UcastReceiver(); + ucast_receiver.start(); + if(log.isDebugEnabled()) log.debug("created unicast receiver thread"); + } + + if(ip_mcast) { + if(mcast_receiver != null) { + if(mcast_receiver.isAlive()) { + if(log.isDebugEnabled()) log.debug("did not create new multicastreceiver thread as existing " + + "multicast receiver thread is still running"); + } + else + mcast_receiver=null; // will be created just below... + } + + if(mcast_receiver == null) { + mcast_receiver=new Thread(this, "UDP mcast receiver"); + mcast_receiver.setPriority(Thread.MAX_PRIORITY); // needed ???? + mcast_receiver.setDaemon(true); + mcast_receiver.start(); + } + } + if(use_outgoing_packet_handler) + outgoing_packet_handler.start(); + if(use_incoming_packet_handler) + incoming_packet_handler.start(); + } + + + /** + * Stops unicast and multicast receiver threads + */ + void stopThreads() { + Thread tmp; + + // 1. Stop the multicast receiver thread + if(mcast_receiver != null) { + if(mcast_receiver.isAlive()) { + tmp=mcast_receiver; + mcast_receiver=null; + closeMulticastSocket(); // will cause the multicast thread to terminate + tmp.interrupt(); + try { + tmp.join(100); + } + catch(Exception e) { + } + tmp=null; + } + mcast_receiver=null; + } + + // 2. Stop the unicast receiver thread + if(ucast_receiver != null) { + ucast_receiver.stop(); + ucast_receiver=null; + } + + // 3. Stop the in_packet_handler thread + if(incoming_packet_handler != null) + incoming_packet_handler.stop(); + + // 4. Stop the outgoing packet handler thread + if(outgoing_packet_handler != null) + outgoing_packet_handler.stop(); + } + + + void handleDownEvent(Event evt) { + switch(evt.getType()) { + + case Event.TMP_VIEW: + case Event.VIEW_CHANGE: + synchronized(members) { + members.removeAllElements(); + Vector tmpvec=((View)evt.getArg()).getMembers(); + for(int i=0; i < tmpvec.size(); i++) + members.addElement(tmpvec.elementAt(i)); + } + break; + + case Event.GET_LOCAL_ADDRESS: // return local address -> Event(SET_LOCAL_ADDRESS, local) + passUp(new Event(Event.SET_LOCAL_ADDRESS, local_addr)); + break; + + case Event.CONNECT: + channel_name=(String)evt.getArg(); + udp_hdr=new UdpHeader(channel_name); + + // removed March 18 2003 (bela), not needed (handled by GMS) + // changed July 2 2003 (bela): we discard CONNECT_OK at the GMS level anyway, this might + // be needed if we run without GMS though + passUp(new Event(Event.CONNECT_OK)); + break; + + case Event.DISCONNECT: + passUp(new Event(Event.DISCONNECT_OK)); + break; + + case Event.CONFIG: + if(log.isDebugEnabled()) log.debug("received CONFIG event: " + evt.getArg()); + handleConfigEvent((HashMap)evt.getArg()); + break; + } + } + + + void handleConfigEvent(HashMap map) { + if(map == null) return; + if(map.containsKey("additional_data")) + additional_data=(byte[])map.get("additional_data"); + if(map.containsKey("send_buf_size")) { + mcast_send_buf_size=((Integer)map.get("send_buf_size")).intValue(); + ucast_send_buf_size=mcast_send_buf_size; + } + if(map.containsKey("recv_buf_size")) { + mcast_recv_buf_size=((Integer)map.get("recv_buf_size")).intValue(); + ucast_recv_buf_size=mcast_recv_buf_size; + } + setBufferSizes(); + } + + + + /* ----------------------------- End of Private Methods ---------------------------------------- */ + + /* ----------------------------- Inner Classes ---------------------------------------- */ + + class IncomingQueueEntry { + IpAddress dest=null; + InetAddress sender=null; + int port=-1; + byte[] buf; + + public IncomingQueueEntry(IpAddress dest, InetAddress sender, int port, byte[] buf) { + this.dest=dest; + this.sender=sender; + this.port=port; + this.buf=buf; + } + + public IncomingQueueEntry(byte[] buf) { + this.buf=buf; + } + } + + + + public class UcastReceiver implements Runnable { + boolean running=true; + Thread thread=null; + + + public void start() { + if(thread == null) { + thread=new Thread(this, "UDP.UcastReceiverThread"); + thread.setDaemon(true); + running=true; + thread.start(); + } + } + + + public void stop() { + Thread tmp; + if(thread != null && thread.isAlive()) { + running=false; + tmp=thread; + thread=null; + closeSocket(); // this will cause the thread to break out of its loop + tmp.interrupt(); + tmp=null; + } + thread=null; + } + + + public void run() { + DatagramPacket packet; + byte receive_buf[]=new byte[65535]; + int len; + byte[] data, tmp; + InetAddress sender_addr; + int sender_port; + + // moved out of loop to avoid excessive object creations (bela March 8 2001) + packet=new DatagramPacket(receive_buf, receive_buf.length); + + while(running && thread != null && sock != null) { + try { + packet.setData(receive_buf, 0, receive_buf.length); + sock.receive(packet); + sender_addr=packet.getAddress(); + sender_port=packet.getPort(); + len=packet.getLength(); + data=packet.getData(); + if(log.isTraceEnabled()) + log.trace(new StringBuilder("received (ucast) ").append(len).append(" bytes from "). + append(sender_addr).append(':').append(sender_port)); + if(len > receive_buf.length) { + if(log.isErrorEnabled()) + log.error("size of the received packet (" + len + ") is bigger than allocated buffer (" + + receive_buf.length + "): will not be able to handle packet. " + + "Use the FRAG protocol and make its frag_size lower than " + receive_buf.length); + } + + if(use_incoming_packet_handler) { + tmp=new byte[len]; + System.arraycopy(data, 0, tmp, 0, len); + incoming_queue.add(new IncomingQueueEntry(local_addr, sender_addr, sender_port, tmp)); + } + else + handleIncomingUdpPacket(local_addr, sender_addr, sender_port, data); + } + catch(SocketException sock_ex) { + if(log.isDebugEnabled()) log.debug("unicast receiver socket is closed, exception=" + sock_ex); + break; + } + catch(InterruptedIOException io_ex) { // thread was interrupted + ; // go back to top of loop, where we will terminate loop + } + catch(Throwable ex) { + if(log.isErrorEnabled()) + log.error("[" + local_addr + "] failed receiving unicast packet", ex); + Util.sleep(100); // so we don't get into 100% cpu spinning (should NEVER happen !) + } + } + if(log.isDebugEnabled()) log.debug("unicast receiver thread terminated"); + } + } + + + /** + * This thread fetches byte buffers from the packet_queue, converts them into messages and passes them up + * to the higher layer (done in handleIncomingUdpPacket()). + */ + class IncomingPacketHandler implements Runnable { + Thread t=null; + + public void run() { + byte[] data; + IncomingQueueEntry entry; + + while(incoming_queue != null && incoming_packet_handler != null) { + try { + entry=(IncomingQueueEntry)incoming_queue.remove(); + data=entry.buf; + } + catch(QueueClosedException closed_ex) { + if(log.isDebugEnabled()) log.debug("packet_handler thread terminating"); + break; + } + handleIncomingUdpPacket(entry.dest, entry.sender, entry.port, data); + } + } + + void start() { + if(t == null || !t.isAlive()) { + t=new Thread(this, "UDP.IncomingPacketHandler thread"); + t.setDaemon(true); + t.start(); + } + } + + void stop() { + if(incoming_queue != null) + incoming_queue.close(false); // should terminate the packet_handler thread too + t=null; + incoming_queue=null; + } + } + + + /** + * This thread fetches byte buffers from the outgoing_packet_queue, converts them into messages and sends them + * using the unicast or multicast socket + */ + class OutgoingPacketHandler implements Runnable { + Thread t=null; + byte[] buf; + DatagramPacket packet; + IpAddress dest; + + public void run() { + Message msg; + + while(outgoing_queue != null && outgoing_packet_handler != null) { + try { + msg=(Message)outgoing_queue.remove(); + handleMessage(msg); + } + catch(QueueClosedException closed_ex) { + break; + } + catch(Throwable th) { + if(log.isErrorEnabled()) log.error("exception sending packet", th); + } + msg=null; // let's give the poor garbage collector a hand... + } + if(log.isTraceEnabled()) log.trace("packet_handler thread terminating"); + } + + protected void handleMessage(Message msg) throws Exception { + send(msg); + } + + + void start() { + if(t == null || !t.isAlive()) { + t=new Thread(this, "UDP.OutgoingPacketHandler thread"); + t.setDaemon(true); + t.start(); + } + } + + void stop() { + if(outgoing_queue != null) + outgoing_queue.close(false); // should terminate the packet_handler thread too + t=null; + // outgoing_queue=null; + } + } + + + + + /** + * Bundles smaller messages into bigger ones. Collects messages in a list until + * messages of a total of max_bundle_size bytes have accumulated, or until + * max_bundle_timeout milliseconds have elapsed, whichever is first. Messages + * are unbundled at the receiver. + */ + class BundlingOutgoingPacketHandler extends OutgoingPacketHandler { + long total_bytes=0; + /** HashMap>. Keys are destinations, values are lists of Messages */ + final HashMap msgs=new HashMap(11); + + + void start() { + super.start(); + t.setName("UDP.BundlingOutgoingPacketHandler thread"); + } + + + public void run() { + Message msg=null, leftover=null; + long start=0; + while(outgoing_queue != null) { + try { + total_bytes=0; + msg=leftover != null? leftover : (Message)outgoing_queue.remove(); // blocks until message is available + start=System.currentTimeMillis(); + leftover=waitForMessagesToAccumulate(msg, outgoing_queue, max_bundle_size, start, max_bundle_timeout); + bundleAndSend(start); + } + catch(QueueClosedException closed_ex) { + break; + } + catch(Throwable th) { + if(log.isErrorEnabled()) log.error("exception sending packet", th); + } + } + bundleAndSend(start); + if(log.isTraceEnabled()) log.trace("packet_handler thread terminating"); + } + + + /** + * Waits until max_size bytes have accumulated in the queue, or max_time milliseconds have elapsed. + * When a message cannot be added to the ready-to-send bundle, it is returned, so the caller can + * re-submit it again next time. + * @param m + * @param q + * @param max_size + * @param max_time + * @return + */ + Message waitForMessagesToAccumulate(Message m, Queue q, long max_size, long start_time, long max_time) { + Message msg, leftover=null; + boolean running=true, size_exceeded=false, time_reached=false; + long len, time_to_wait=max_time, waited_time=0; + + while(running) { + try { + msg=m != null? m : (Message)q.remove(time_to_wait); + m=null; // necessary, otherwise we get 'm' again in subsequent iterations of the same loop ! + len=msg.size(); + checkLength(len); + waited_time=System.currentTimeMillis() - start_time; + time_to_wait=max_time - waited_time; + size_exceeded=total_bytes + len > max_size; + time_reached=time_to_wait <= 0; + + if(size_exceeded) { + running=false; + leftover=msg; + } + else { + addMessage(msg); + total_bytes+=len; + if(time_reached) + running=false; + } + } + catch(TimeoutException timeout) { + waited_time=System.currentTimeMillis() - start_time; + time_reached=true; + break; + } + catch(QueueClosedException closed) { + break; + } + catch(Exception ex) { + log.error("failure in bundling", ex); + } + } + return leftover; + } + + + void checkLength(long len) throws Exception { + if(len > max_bundle_size) + throw new Exception("UDP.BundlingOutgoingPacketHandler.handleMessage(): message size (" + len + + ") is greater than max bundling size (" + max_bundle_size + "). " + + "Set the fragmentation/bundle size in FRAG and UDP correctly"); + } + + + void addMessage(Message msg) { + List tmp; + Address dst=msg.getDest(); + synchronized(msgs) { + tmp=(List)msgs.get(dst); + if(tmp == null) { + tmp=new List(); + msgs.put(dst, tmp); + } + tmp.add(msg); + } + } + + + + private void bundleAndSend(long start_time) { + Map.Entry entry; + IpAddress dst; + Buffer buffer; + InetAddress addr; + int port; + List l; + long stop_time=System.currentTimeMillis(); + + synchronized(msgs) { + if(msgs.size() == 0) + return; + if(start_time == 0) + start_time=System.currentTimeMillis(); + + if(log.isTraceEnabled()) { + StringBuilder sb=new StringBuilder("sending ").append(numMsgs(msgs)).append(" msgs ("); + sb.append(total_bytes).append(" bytes, ").append(stop_time-start_time).append("ms)"); + sb.append(" to ").append(msgs.size()).append(" destination(s)"); + if(msgs.size() > 1) sb.append(" (dests=").append(msgs.keySet()).append(")"); + log.trace(sb.toString()); + } + for(Iterator it=msgs.entrySet().iterator(); it.hasNext();) { + entry=(Map.Entry)it.next(); + dst=(IpAddress)entry.getKey(); + addr=dst.getIpAddress(); + port=dst.getPort(); + l=(List)entry.getValue(); + try { + if(l.size() > 0) { + synchronized(out_stream) { + buffer=listToBuffer(l, dst); + doSend(buffer, addr, port); + } + } + } + catch(Exception e) { + if(log.isErrorEnabled()) log.error("exception sending msg (to dest=" + dst + ")", e); + } + } + msgs.clear(); + } + } + + private int numMsgs(HashMap map) { + Collection values=map.values(); + List l; + int size=0; + for(Iterator it=values.iterator(); it.hasNext();) { + l=(List)it.next(); + size+=l.size(); + } + return size; + } + } + + + String dumpMessages(HashMap map) { + StringBuilder sb=new StringBuilder(); + Map.Entry entry; + List l; + Object key; + if(map != null) { + synchronized(map) { + for(Iterator it=map.entrySet().iterator(); it.hasNext();) { + entry=(Map.Entry)it.next(); + key=entry.getKey(); + if(key == null) + key="null"; + l=(List)entry.getValue(); + sb.append(key).append(": "); + sb.append(l.size()).append(" msgs\n"); + } + } + } + return sb.toString(); + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/obsolete/UDP_NIO.java.txt =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/obsolete/UDP_NIO.java.txt,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/obsolete/UDP_NIO.java.txt 17 Aug 2012 14:51:27 -0000 1.1 @@ -0,0 +1,1512 @@ +package org.jgroups.protocols; + +import org.jgroups.*; +import org.jgroups.stack.LogicalAddress; +import org.jgroups.stack.Protocol; +import org.jgroups.util.Queue; +import org.jgroups.util.QueueClosedException; +import org.jgroups.util.Util; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.*; +import java.net.*; +import java.util.*; + +/** + * Multicast transport. Similar to UDP, but binds to multiple (or all) interfaces for sending and receiving + * multicast and unicast traffic.
        + * The list of interfaces can be set via a property (comma-delimited list of IP addresses or "all" for all + * interfaces). Note that this class only works under JDK 1.4 and higher.
        + * For each of the interfaces listed we create a Listener, which listens on the group multicast address and creates + * a unicast datagram socket. The address of this member is determined at startup time, and is the host name plus + * a timestamp (LogicalAddress). It does not change during the lifetime of the process. The LogicalAddress contains + * a list of all unicast socket addresses to which we can send back unicast messages. When we send a message, the + * Listener adds the sender's return address. When we receive a message, we add that address to our routing cache, which + * contains logical addresses and physical addresses. When we need to send a unicast address, we first check whether + * the logical address has a physical address associated with it in the cache. If so, we send a message to that address. + * If not, we send the unicast message to all physical addresses contained in the LogicalAddress.
        + * UDP_NIO guarantees that - in scenarios with multiple subnets and multi-homed machines - members do see each other. + * There is some overhead in multicasting the same message on multiple interfaces, and potentially sending a unicast + * on multiple interfaces as well, but the advantage is that we don't need stuff like bind_addr any longer. Plus, + * the unicast routing caches should ensure that unicasts are only sent via 1 interface in almost all cases. + * + * @author Bela Ban Oct 2003 + * @version $Id: UDP_NIO.java.txt,v 1.1 2012/08/17 14:51:27 marcin Exp $ + * @deprecated Use UDP instead with send_interfaces or receive_interfaces properties defined + */ +public class UDP_NIO extends Protocol implements Receiver { + + static final String name="UDP_NIO"; + + /** Maintains a list of Connectors, one for each interface we're listening on */ + ConnectorTable ct=null; + + /** A List of bind addresses, we create 1 Connector for each interface */ + List bind_addrs=null; + + /** The name of the group to which this member is connected */ + String group_name=null; + + /** The multicast address (mcast address and port) this member uses (default: 230.1.2.3:7500) */ + InetSocketAddress mcast_addr=null; + + /** The address of this member. Valid for the lifetime of the JVM in which this member runs */ + LogicalAddress local_addr=new LogicalAddress(null, null); + + /** Logical address without list of physical addresses */ + LogicalAddress local_addr_canonical=local_addr.copy(); + + /** Pre-allocated byte stream. Used for serializing datagram packets */ + ByteArrayOutputStream out_stream=new ByteArrayOutputStream(65535); + + /** + * The port to which the unicast receiver socket binds. + * 0 means to bind to any (ephemeral) port + */ + int local_bind_port=0; + int port_range=1; // 27-6-2003 bgooren, Only try one port by default + + + /** + * Whether to enable IP multicasting. If false, multiple unicast datagram + * packets are sent rather than one multicast packet + */ + boolean ip_mcast=true; + + /** The time-to-live (TTL) for multicast datagram packets */ + int ip_ttl=32; + + /** The members of this group (updated when a member joins or leaves) */ + Vector members=new Vector(); + + /** + * Header to be added to all messages sent via this protocol. It is + * preallocated for efficiency + */ + UdpHeader udp_hdr=null; + + /** Send buffer size of the multicast datagram socket */ + int mcast_send_buf_size=300000; + + /** Receive buffer size of the multicast datagram socket */ + int mcast_recv_buf_size=300000; + + /** Send buffer size of the unicast datagram socket */ + int ucast_send_buf_size=300000; + + /** Receive buffer size of the unicast datagram socket */ + int ucast_recv_buf_size=300000; + + /** + * If true, messages sent to self are treated specially: unicast messages are + * looped back immediately, multicast messages get a local copy first and - + * when the real copy arrives - it will be discarded. Useful for Window + * media (non)sense + * @deprecated This is used by default now + */ + boolean loopback=true; //todo: remove + + /** + * Sometimes receivers are overloaded (they have to handle de-serialization etc). + * Packet handler is a separate thread taking care of de-serialization, receiver + * thread(s) simply put packet in queue and return immediately. Setting this to + * true adds one more thread + */ + boolean use_packet_handler=false; + + /** Used by packet handler to store incoming DatagramPackets */ + Queue packet_queue=null; + + /** + * If set it will be added to local_addr. Used to implement + * for example transport independent addresses + */ + byte[] additional_data=null; + + /** + * Dequeues DatagramPackets from packet_queue, unmarshalls them and + * calls handleIncomingUdpPacket() + */ + PacketHandler packet_handler=null; + + + /** Number of bytes to allocate to receive a packet. Needs to be set to be higher than frag_size + * (handle CONFIG event) + */ + static final int DEFAULT_RECEIVE_BUFFER_SIZE=120000; // todo: make settable and/or use CONFIG event + + + + + /** + * Creates the UDP_NIO protocol, and initializes the + * state variables, does however not start any sockets or threads. + */ + public UDP_NIO() { + } + + /** + * debug only + */ + public String toString() { + return "Protocol UDP(local address: " + local_addr + ')'; + } + + + public void receive(DatagramPacket packet) { + int len=packet.getLength(); + byte[] data=packet.getData(); + SocketAddress sender=packet.getSocketAddress(); + + if(len == 4) { // received a diagnostics probe + if(data[0] == 'd' && data[1] == 'i' && data[2] == 'a' && data[3] == 'g') { + handleDiagnosticProbe(sender); + return; + } + } + + if(trace) + log.trace("received " + len + " bytes from " + sender); + + if(use_packet_handler && packet_queue != null) { + byte[] tmp=new byte[len]; + System.arraycopy(data, 0, tmp, 0, len); + try { + Object[] arr=new Object[]{tmp, sender}; + packet_queue.add(arr); + return; + } + catch(QueueClosedException e) { + if(warn) log.warn("packet queue for packet handler thread is closed"); + // pass through to handleIncomingPacket() + } + } + + handleIncomingUdpPacket(data, sender); + } + + + /* ----------------------- Receiving of MCAST UDP packets ------------------------ */ + +// public void run() { +// DatagramPacket packet; +// byte receive_buf[]=new byte[65000]; +// int len; +// byte[] tmp1, tmp2; +// +// // moved out of loop to avoid excessive object creations (bela March 8 2001) +// packet=new DatagramPacket(receive_buf, receive_buf.length); +// +// while(mcast_receiver != null && mcast_sock != null) { +// try { +// packet.setData(receive_buf, 0, receive_buf.length); +// mcast_sock.receive(packet); +// len=packet.getLength(); +// if(len == 1 && packet.getData()[0] == 0) { +// if(trace) if(log.isInfoEnabled()) log.info("UDP_NIO.run()", "received dummy packet"); +// continue; +// } +// +// if(len == 4) { // received a diagnostics probe +// byte[] tmp=packet.getData(); +// if(tmp[0] == 'd' && tmp[1] == 'i' && tmp[2] == 'a' && tmp[3] == 'g') { +// handleDiagnosticProbe(null, null); +// continue; +// } +// } +// +// if(trace) +// if(log.isInfoEnabled()) log.info("UDP_NIO.receive()", "received (mcast) " + packet.getLength() + " bytes from " + +// packet.getAddress() + ":" + packet.getPort() + " (size=" + len + " bytes)"); +// if(len > receive_buf.length) { +// if(log.isErrorEnabled()) log.error("UDP_NIO.run()", "size of the received packet (" + len + ") is bigger than " + +// "allocated buffer (" + receive_buf.length + "): will not be able to handle packet. " + +// "Use the FRAG protocol and make its frag_size lower than " + receive_buf.length); +// } +// +// if(Version.compareTo(packet.getData()) == false) { +// if(warn) log.warn("UDP_NIO.run()", +// "packet from " + packet.getAddress() + ":" + packet.getPort() + +// " has different version (" + +// Version.printVersionId(packet.getData(), Version.version_id.length) + +// ") from ours (" + Version.printVersionId(Version.version_id) + +// "). This may cause problems"); +// } +// +// if(use_packet_handler) { +// tmp1=packet.getData(); +// tmp2=new byte[len]; +// System.arraycopy(tmp1, 0, tmp2, 0, len); +// packet_queue.add(tmp2); +// } else +// handleIncomingUdpPacket(packet.getData()); +// } catch(SocketException sock_ex) { +// if(log.isInfoEnabled()) log.info("UDP_NIO.run()", "multicast socket is closed, exception=" + sock_ex); +// break; +// } catch(InterruptedIOException io_ex) { // thread was interrupted +// ; // go back to top of loop, where we will terminate loop +// } catch(Throwable ex) { +// if(log.isErrorEnabled()) log.error("UDP_NIO.run()", "exception=" + ex + ", stack trace=" + Util.printStackTrace(ex)); +// Util.sleep(1000); // so we don't get into 100% cpu spinning (should NEVER happen !) +// } +// } +// if(log.isInfoEnabled()) log.info("UDP_NIO.run()", "multicast thread terminated"); +// } + + void handleDiagnosticProbe(SocketAddress sender) { + try { + byte[] diag_rsp=getDiagResponse().getBytes(); + DatagramPacket rsp=new DatagramPacket(diag_rsp, 0, diag_rsp.length, sender); + + if(log.isInfoEnabled()) log.info("sending diag response to " + sender); + ct.send(rsp); + } catch(Throwable t) { + if(log.isErrorEnabled()) log.error("failed sending diag rsp to " + sender + ", exception=" + t); + } + } + + String getDiagResponse() { + StringBuilder sb=new StringBuilder(); + sb.append(local_addr).append(" (").append(group_name).append(')'); + sb.append(" [").append(mcast_addr).append("]\n"); + sb.append("Version=").append(Version.description).append(", cvs=\"").append(Version.cvs).append("\"\n"); + sb.append("physical addresses: ").append(local_addr.getPhysicalAddresses()).append('\n'); + sb.append("members: ").append(members).append('\n'); + + return sb.toString(); + } + + /* ------------------------------------------------------------------------------- */ + + + + /*------------------------------ Protocol interface ------------------------------ */ + + public String getName() { + return name; + } + + + public void init() throws Exception { + if(use_packet_handler) { + packet_queue=new Queue(); + packet_handler=new PacketHandler(); + } + } + + + /** + * Creates the unicast and multicast sockets and starts the unicast and multicast receiver threads + */ + public void start() throws Exception { + if(log.isInfoEnabled()) log.info("creating sockets and starting threads"); + if(ct == null) { + ct=new ConnectorTable(mcast_addr, DEFAULT_RECEIVE_BUFFER_SIZE, mcast_recv_buf_size, ip_mcast, this); + + for(Iterator it=bind_addrs.iterator(); it.hasNext();) { + String bind_addr=(String)it.next(); + ct.listenOn(bind_addr, local_bind_port, port_range, DEFAULT_RECEIVE_BUFFER_SIZE, ucast_recv_buf_size, + ucast_send_buf_size, ip_ttl, this); + } + + // add physical addresses to local_addr + List physical_addrs=ct.getConnectorAddresses(); // must be non-null and size() >= 1 + for(Iterator it=physical_addrs.iterator(); it.hasNext();) { + SocketAddress address=(SocketAddress)it.next(); + local_addr.addPhysicalAddress(address); + } + + if(additional_data != null) + local_addr.setAdditionalData(additional_data); + + ct.start(); + + passUp(new Event(Event.SET_LOCAL_ADDRESS, local_addr)); + if(use_packet_handler) + packet_handler.start(); + } + } + + + public void stop() { + if(log.isInfoEnabled()) log.info("closing sockets and stopping threads"); + if(packet_handler != null) + packet_handler.stop(); + if(ct != null) { + ct.stop(); + ct=null; + } + local_addr.removeAllPhysicalAddresses(); + } + + + /** + * Setup the Protocol instance acording to the configuration string. + * The following properties are being read by the UDP protocol: + *
          + *
        • param mcast_addr - the multicast address to use default is 224.0.0.200 + *
        • param mcast_port - (int) the port that the multicast is sent on default is 7500 + *
        • param ip_mcast - (boolean) flag whether to use IP multicast - default is true + *
        • param ip_ttl - Set the default time-to-live for multicast packets sent out on this socket. default is 32 + *
        + * @return true if no other properties are left. + * false if the properties still have data in them, ie , + * properties are left over and not handled by the protocol stack + */ + public boolean setProperties(Properties props) { + String str; + List exclude_list=null; + String mcast_addr_name="230.8.8.8"; + int mcast_port=7500; + + super.setProperties(props); + str=props.getProperty("bind_addrs"); + if(str != null) { + str=str.trim(); + if("all".equals(str.toLowerCase())) { + try { + bind_addrs=determineAllBindInterfaces(); + } + catch(SocketException e) { + e.printStackTrace(); + bind_addrs=null; + } + } + else { + bind_addrs=Util.parseCommaDelimitedStrings(str); + } + props.remove("bind_addrs"); + } + + str=props.getProperty("bind_addrs_exclude"); + if(str != null) { + str=str.trim(); + exclude_list=Util.parseCommaDelimitedStrings(str); + props.remove("bind_addrs_exclude"); + } + + str=props.getProperty("bind_port"); + if(str != null) { + local_bind_port=Integer.parseInt(str); + props.remove("bind_port"); + } + + str=props.getProperty("start_port"); + if(str != null) { + local_bind_port=Integer.parseInt(str); + props.remove("start_port"); + } + + str=props.getProperty("port_range"); + if(str != null) { + port_range=Integer.parseInt(str); + props.remove("port_range"); + } + + str=props.getProperty("mcast_addr"); + if(str != null) { + mcast_addr_name=str; + props.remove("mcast_addr"); + } + + str=props.getProperty("mcast_port"); + if(str != null) { + mcast_port=Integer.parseInt(str); + props.remove("mcast_port"); + } + + str=props.getProperty("ip_mcast"); + if(str != null) { + ip_mcast=Boolean.valueOf(str).booleanValue(); + props.remove("ip_mcast"); + } + + str=props.getProperty("ip_ttl"); + if(str != null) { + ip_ttl=Integer.parseInt(str); + props.remove("ip_ttl"); + } + + str=props.getProperty("mcast_send_buf_size"); + if(str != null) { + mcast_send_buf_size=Integer.parseInt(str); + props.remove("mcast_send_buf_size"); + } + + str=props.getProperty("mcast_recv_buf_size"); + if(str != null) { + mcast_recv_buf_size=Integer.parseInt(str); + props.remove("mcast_recv_buf_size"); + } + + str=props.getProperty("ucast_send_buf_size"); + if(str != null) { + ucast_send_buf_size=Integer.parseInt(str); + props.remove("ucast_send_buf_size"); + } + + str=props.getProperty("ucast_recv_buf_size"); + if(str != null) { + ucast_recv_buf_size=Integer.parseInt(str); + props.remove("ucast_recv_buf_size"); + } + + str=props.getProperty("use_packet_handler"); + if(str != null) { + use_packet_handler=Boolean.valueOf(str).booleanValue(); + props.remove("use_packet_handler"); + } + + + // determine mcast_addr + mcast_addr=new InetSocketAddress(mcast_addr_name, mcast_port); + + // handling of bind_addrs + if(bind_addrs == null) + bind_addrs=new ArrayList(); + if(bind_addrs.size() == 0) { + try { + String default_bind_addr=determineDefaultBindInterface(); + bind_addrs.add(default_bind_addr); + } + catch(SocketException ex) { + if(log.isErrorEnabled()) log.error("failed determining the default bind interface: " + ex); + } + } + if(exclude_list != null) { + bind_addrs.removeAll(exclude_list); + } + if(bind_addrs.size() == 0) { + if(log.isErrorEnabled()) log.error("no valid bind interface found, unable to listen for network traffic"); + return false; + } + else { + + if(log.isInfoEnabled()) log.info("bind interfaces are " + bind_addrs); + } + + if(props.size() > 0) { + log.error("UDP_NIO.setProperties(): the following properties are not recognized: " + props); + + return false; + } + return true; + } + + + /** + * This prevents the up-handler thread to be created, which essentially is superfluous: + * messages are received from the network rather than from a layer below. + * DON'T REMOVE ! + */ + public void startUpHandler() { + } + + /** + * handle the UP event. + * + * @param evt - the event being send from the stack + */ + public void up(Event evt) { + passUp(evt); + + switch(evt.getType()) { + + case Event.CONFIG: + passUp(evt); + if(log.isInfoEnabled()) log.info("received CONFIG event: " + evt.getArg()); + handleConfigEvent((HashMap)evt.getArg()); + return; + } + + passUp(evt); + } + + /** + * Caller by the layer above this layer. Usually we just put this Message + * into the send queue and let one or more worker threads handle it. A worker thread + * then removes the Message from the send queue, performs a conversion and adds the + * modified Message to the send queue of the layer below it, by calling Down). + */ + public void down(Event evt) { + Message msg; + Object dest_addr; + + if(evt.getType() != Event.MSG) { // unless it is a message handle it and respond + handleDownEvent(evt); + return; + } + + msg=(Message)evt.getArg(); + + if(udp_hdr != null && udp_hdr.channel_name != null) { + // added patch by Roland Kurmann (March 20 2003) + msg.putHeader(name, udp_hdr); + } + + dest_addr=msg.getDest(); + + // Because we don't call Protocol.passDown(), we notify the observer directly (e.g. PerfObserver). + // This way, we still have performance numbers for UDP + if(observer != null) + observer.passDown(evt); + + if(dest_addr == null) { // 'null' means send to all group members + if(ip_mcast == false) { + //sends a separate UDP message to each address + sendMultipleUdpMessages(msg, members); + return; + } + } + + try { + sendUdpMessage(msg); // either unicast (dest != null) or multicast (dest == null) + } + catch(Exception e) { + if(log.isErrorEnabled()) log.error("exception=" + e + ", msg=" + msg + ", mcast_addr=" + mcast_addr); + } + } + + + + + + + /*--------------------------- End of Protocol interface -------------------------- */ + + + /* ------------------------------ Private Methods -------------------------------- */ + + + void handleMessage(Message msg) { + + } + + + /** + * Processes a packet read from either the multicast or unicast socket. Needs to be synchronized because + * mcast or unicast socket reads can be concurrent + */ + void handleIncomingUdpPacket(byte[] data, SocketAddress sender) { + ByteArrayInputStream inp_stream; + ObjectInputStream inp; + Message msg=null; + UdpHeader hdr=null; + Event evt; + Address dst, src; + short version; + + try { + // skip the first n bytes (default: 4), this is the version info + inp_stream=new ByteArrayInputStream(data); + inp=new ObjectInputStream(inp_stream); + version=inp.readShort(); + + if(Version.compareTo(version) == false) { + if(warn) + log.warn("packet from " + sender + " has different version (" + version + + ") from ours (" + Version.version + "). This may cause problems"); + } + + msg=new Message(); + msg.readExternal(inp); + dst=msg.getDest(); + src=msg.getSrc(); + if(src == null) { + if(log.isErrorEnabled()) log.error("sender's address is null"); + } + else { + ((LogicalAddress)src).setPrimaryPhysicalAddress(sender); + } + + // discard my own multicast loopback copy + if((dst == null || dst.isMulticastAddress()) && src != null && local_addr.equals(src)) { + if(trace) + log.trace("discarded own loopback multicast packet"); + + // System.out.println("-- discarded " + msg.getObject()); + + return; + } + + evt=new Event(Event.MSG, msg); + if(trace) + log.trace("Message is " + msg + ", headers are " + msg.getHeaders()); + + /* Because Protocol.up() is never called by this bottommost layer, we call up() directly in the observer. + * This allows e.g. PerfObserver to get the time of reception of a message */ + if(observer != null) + observer.up(evt, up_queue.size()); + + hdr=(UdpHeader)msg.removeHeader(name); + } catch(Throwable e) { + if(log.isErrorEnabled()) log.error("exception=" + Util.getStackTrace(e)); + return; + } + + if(hdr != null) { + + /* Discard all messages destined for a channel with a different name */ + String ch_name=null; + + if(hdr.channel_name != null) + ch_name=hdr.channel_name; + + // Discard if message's group name is not the same as our group name unless the + // message is a diagnosis message (special group name DIAG_GROUP) + if(ch_name != null && group_name != null && !group_name.equals(ch_name) && + !ch_name.equals(Util.DIAG_GROUP)) { + + if(warn) log.warn("discarded message from different group (" + + ch_name + "). Sender was " + msg.getSrc()); + return; + } + } + + passUp(evt); + } + + + /** + * Send a message to the address specified in dest + */ + void sendUdpMessage(Message msg) throws Exception { + Address dest, src; + ObjectOutputStream out; + byte buf[]; + DatagramPacket packet; + Message copy; + Event evt; // for loopback messages + + dest=msg.getDest(); // if non-null: unicast, else multicast + src=msg.getSrc(); + if(src == null) { + src=local_addr_canonical; // no physical addresses present + msg.setSrc(src); + } + + if(trace) + log.trace("sending message to " + msg.getDest() + + " (src=" + msg.getSrc() + "), headers are " + msg.getHeaders()); + + // Don't send if destination is local address. Instead, switch dst and src and put in up_queue. + // If multicast message, loopback a copy directly to us (but still multicast). Once we receive this, + // we will discard our own multicast message + if(dest == null || dest.isMulticastAddress() || dest.equals(local_addr)) { + copy=msg.copy(); + copy.removeHeader(name); + evt=new Event(Event.MSG, copy); + + /* Because Protocol.up() is never called by this bottommost layer, we call up() directly in the observer. + This allows e.g. PerfObserver to get the time of reception of a message */ + if(observer != null) + observer.up(evt, up_queue.size()); + if(trace) log.trace("looped back local message " + copy); + + // System.out.println("\n-- passing up packet id=" + copy.getObject()); + passUp(evt); + // System.out.println("-- passed up packet id=" + copy.getObject()); + + if(dest != null && !dest.isMulticastAddress()) + return; // it is a unicast message to myself, no need to put on the network + } + + out_stream.reset(); + out=new ObjectOutputStream(out_stream); + out.writeShort(Version.version); + msg.writeExternal(out); + out.flush(); // needed if out buffers its output to out_stream + buf=out_stream.toByteArray(); + packet=new DatagramPacket(buf, buf.length, mcast_addr); + + //System.out.println("-- sleeping 4 secs"); + // Thread.sleep(4000); + + + // System.out.println("\n-- sending packet " + msg.getObject()); + ct.send(packet); + // System.out.println("-- sent " + msg.getObject()); + } + + + void sendMultipleUdpMessages(Message msg, Vector dests) { + Address dest; + + for(int i=0; i < dests.size(); i++) { + dest=(Address)dests.elementAt(i); + msg.setDest(dest); + + try { + sendUdpMessage(msg); + } + catch(Exception e) { + if(log.isDebugEnabled()) log.debug("exception=" + e); + } + } + } + + + + + +// +// /** +// * Workaround for the problem encountered in certains JDKs that a thread listening on a socket +// * cannot be interrupted. Therefore we just send a dummy datagram packet so that the thread 'wakes up' +// * and realizes it has to terminate. Should be removed when all JDKs support Thread.interrupt() on +// * reads. Uses send_sock t send dummy packet, so this socket has to be still alive. +// * +// * @param dest The destination host. Will be local host if null +// * @param port The destination port +// */ +// void sendDummyPacket(InetAddress dest, int port) { +// DatagramPacket packet; +// byte[] buf={0}; +// +// if(dest == null) { +// try { +// dest=InetAddress.getLocalHost(); +// } catch(Exception e) { +// } +// } +// +// if(trace) if(log.isInfoEnabled()) log.info("UDP_NIO.sendDummyPacket()", "sending packet to " + dest + ":" + port); +// +// if(ucast_sock == null || dest == null) { +// if(warn) log.warn("UDP_NIO.sendDummyPacket()", "send_sock was null or dest was null, cannot send dummy packet"); +// return; +// } +// packet=new DatagramPacket(buf, buf.length, dest, port); +// try { +// ucast_sock.send(packet); +// } catch(Throwable e) { +// if(log.isErrorEnabled()) log.error("UDP_NIO.sendDummyPacket()", "exception sending dummy packet to " + +// dest + ":" + port + ": " + e); +// } +// } + + + + + + void handleDownEvent(Event evt) { + switch(evt.getType()) { + + case Event.TMP_VIEW: + case Event.VIEW_CHANGE: + synchronized(members) { + members.removeAllElements(); + Vector tmpvec=((View)evt.getArg()).getMembers(); + for(int i=0; i < tmpvec.size(); i++) + members.addElement(tmpvec.elementAt(i)); + } + break; + + case Event.GET_LOCAL_ADDRESS: // return local address -> Event(SET_LOCAL_ADDRESS, local) + passUp(new Event(Event.SET_LOCAL_ADDRESS, local_addr)); + break; + + case Event.CONNECT: + group_name=(String)evt.getArg(); + udp_hdr=new UdpHeader(group_name); + + // removed March 18 2003 (bela), not needed (handled by GMS) + // changed July 2 2003 (bela): we discard CONNECT_OK at the GMS level anyway, this might + // be needed if we run without GMS though + passUp(new Event(Event.CONNECT_OK)); + break; + + case Event.DISCONNECT: + passUp(new Event(Event.DISCONNECT_OK)); + break; + + case Event.CONFIG: + if(log.isInfoEnabled()) log.info("received CONFIG event: " + evt.getArg()); + handleConfigEvent((HashMap)evt.getArg()); + break; + } + } + + + void handleConfigEvent(HashMap map) { + if(map == null) return; + if(map.containsKey("additional_data")) + additional_data=(byte[])map.get("additional_data"); + if(map.containsKey("send_buf_size")) { + mcast_send_buf_size=((Integer)map.get("send_buf_size")).intValue(); + ucast_send_buf_size=mcast_send_buf_size; + } + if(map.containsKey("recv_buf_size")) { + mcast_recv_buf_size=((Integer)map.get("recv_buf_size")).intValue(); + ucast_recv_buf_size=mcast_recv_buf_size; + } + } + + + /** Return the first non-loopback interface */ + public String determineDefaultBindInterface() throws SocketException { + for(Enumeration en=NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) { + NetworkInterface ni=(NetworkInterface)en.nextElement(); + for(Enumeration en2=ni.getInetAddresses(); en2.hasMoreElements();) { + InetAddress bind_addr=(InetAddress)en2.nextElement(); + if(!bind_addr.isLoopbackAddress()) { + return bind_addr.getHostAddress(); + } + } + } + return null; + } + + public List determineAllBindInterfaces() throws SocketException { + List ret=new ArrayList(); + for(Enumeration en=NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) { + NetworkInterface ni=(NetworkInterface)en.nextElement(); + for(Enumeration en2=ni.getInetAddresses(); en2.hasMoreElements();) { + InetAddress bind_addr=(InetAddress)en2.nextElement(); + ret.add(bind_addr.getHostAddress()); + } + } + + return ret; + } + + /* ----------------------------- End of Private Methods ---------------------------------------- */ + + + + /* ----------------------------- Inner Classes ---------------------------------------- */ + + + /** + * This thread fetches byte buffers from the packet_queue, converts them into messages and passes them up + * to the higher layer (done in handleIncomingUdpPacket()). + */ + class PacketHandler implements Runnable { + Thread t=null; + + public void run() { + byte[] data; + SocketAddress sender; + + while(packet_queue != null && packet_handler != null) { + try { + Object[] arr=(Object[])packet_queue.remove(); + data=(byte[])arr[0]; + sender=(SocketAddress)arr[1]; + } catch(QueueClosedException closed_ex) { + if(log.isInfoEnabled()) log.info("packet_handler thread terminating"); + break; + } + handleIncomingUdpPacket(data, sender); + data=null; // let's give the poor garbage collector a hand... + } + } + + void start() { + if(t == null) { + t=new Thread(this, "UDP_NIO.PacketHandler thread"); + t.setDaemon(true); + t.start(); + } + } + + void stop() { + if(packet_queue != null) + packet_queue.close(false); // should terminate the packet_handler thread too + t=null; + packet_queue=null; + } + } + + + + + /** + * Manages a multicast and unicast socket on a given interface (NIC). The multicast socket is used + * to listen for incoming multicast packets, the unicast socket is used to (1) listen for incoming + * unicast packets, (2) to send unicast packets and (3) to send multicast packets + */ + public static class Connector implements Runnable { + + protected Thread t=null; + + protected SenderThread sender_thread=null; + + /** Interface on which ucast_sock and mcast_sender_sock are created */ + NetworkInterface bind_interface; + + + /** Used for sending/receiving unicast/multicast packets. The reason we have to use a MulticastSocket versus a + * DatagramSocket is that only MulticastSockets allow to set the interface over which a multicast + * is sent: DatagramSockets consult the routing table to find the interface + */ + MulticastSocket mcast_sock=null; + + /** Local port of the mcast_sock */ + SocketAddress localAddr=null; + + /** The receiver which handles incoming packets */ + Receiver receiver=null; + + /** Buffer for incoming unicast packets */ + protected byte[] receive_buffer=null; + + + Queue send_queue=new Queue(); + + static final Log mylog=LogFactory.getLog(Connector.class); + static final boolean mywarn=mylog.isWarnEnabled(); + + + class SenderThread extends Thread { + + public SenderThread() { + super(Util.getGlobalThreadGroup(), "SenderThread"); + } + + public void run() { + Object[] arr; + byte[] buf; + SocketAddress dest; + + while(send_queue != null) { + try { + arr=(Object[])send_queue.remove(); + buf=(byte[])arr[0]; + dest=(SocketAddress)arr[1]; + mcast_sock.send(new DatagramPacket(buf, buf.length, dest)); + } + catch(QueueClosedException e) { + break; + } + catch(SocketException e) { + e.printStackTrace(); + } + catch(IOException e) { + e.printStackTrace(); + } + + } + } + } + + + + public Connector(NetworkInterface bind_interface, int local_bind_port, + int port_range, int receive_buffer_size, + int receive_sock_buf_size, int send_sock_buf_size, + int ip_ttl, Receiver receiver) throws IOException { + this.bind_interface=bind_interface; + this.receiver=receiver; + this.receive_buffer=new byte[receive_buffer_size]; + + mcast_sock=createMulticastSocket(local_bind_port, port_range); + + // changed Bela Dec 31 2003: if loopback is disabled other members on the same machine won't be able + // to receive our multicasts + // mcast_sock.setLoopbackMode(true); // we don't want to receive our own multicasts + mcast_sock.setReceiveBufferSize(receive_sock_buf_size); + mcast_sock.setSendBufferSize(send_sock_buf_size); + mcast_sock.setTimeToLive(ip_ttl); + System.out.println("ttl=" + mcast_sock.getTimeToLive()); + mcast_sock.setNetworkInterface(this.bind_interface); // for outgoing multicasts + localAddr=mcast_sock.getLocalSocketAddress(); + System.out.println("-- local_addr=" + localAddr); + System.out.println("-- mcast_sock: send_bufsize=" + mcast_sock.getSendBufferSize() + + ", recv_bufsize=" + mcast_sock.getReceiveBufferSize()); + } + + + public SocketAddress getLocalAddress() { + return localAddr; + } + + public NetworkInterface getBindInterface() { + return bind_interface; + } + + public void start() throws Exception { + if(mcast_sock == null) + throw new Exception("UDP_NIO.Connector.start(): connector has been stopped (start() cannot be called)"); + + if(t != null && t.isAlive()) { + if(mywarn) mylog.warn("connector thread is already running"); + return; + } + t=new Thread(this, "ConnectorThread for " + localAddr); + t.setDaemon(true); + t.start(); + + sender_thread=new SenderThread(); + sender_thread.start(); + } + + /** Stops the connector. After this call, start() cannot be called, but a new connector has to + * be created + */ + public void stop() { + if(mcast_sock != null) + mcast_sock.close(); // terminates the thread if running + t=null; + mcast_sock=null; + } + + + + /** Sends a message using mcast_sock */ + public void send(DatagramPacket packet) throws Exception { + //mcast_sock.send(packet); + + byte[] buf=(byte[])packet.getData().clone(); + Object[] arr=new Object[]{buf, packet.getSocketAddress()}; + send_queue.add(arr); + } + + public void run() { + DatagramPacket packet=new DatagramPacket(receive_buffer, receive_buffer.length); + while(t != null) { + try { + packet.setData(receive_buffer, 0, receive_buffer.length); + ConnectorTable.receivePacket(packet, mcast_sock, receiver); + } + catch(Throwable th) { + if(th == null || mcast_sock == null || mcast_sock.isClosed()) + break; + if(mylog.isErrorEnabled()) mylog.error("[" + localAddr + "] exception=" + th); + Util.sleep(300); // so we don't get into 100% cpu spinning (should NEVER happen !) + } + } + t=null; + } + + + + + public String toString() { + StringBuilder sb=new StringBuilder(); + sb.append("local_addr=").append(localAddr).append(", mcast_group="); + return sb.toString(); + } + + + + + + // 27-6-2003 bgooren, find available port in range (start_port, start_port+port_range) + private MulticastSocket createMulticastSocket(int local_bind_port, int port_range) throws IOException { + MulticastSocket sock=null; + int tmp_port=local_bind_port; + + int max_port=tmp_port + port_range; + while(tmp_port <= max_port) { + try { + sock=new MulticastSocket(tmp_port); + break; + } + catch(Exception bind_ex) { + tmp_port++; + } + } + if(sock == null) + throw new IOException("could not create a MulticastSocket (port range: " + local_bind_port + + " - " + (local_bind_port+port_range)); + return sock; + } + } + + + + + + /** Manages a bunch of Connectors */ + public static class ConnectorTable implements Receiver, Runnable { + + Thread t=null; + + /** Socket to receive multicast packets. Will be bound to n interfaces */ + MulticastSocket mcast_sock=null; + + /** The multicast address which mcast_sock will join (e.g. 230.1.2.3:7500) */ + InetSocketAddress mcastAddr=null; + + Receiver receiver=null; + + /** Buffer for incoming packets */ + byte[] receive_buffer=null; + + /** Vector. A list of Connectors, one for each interface we listen on */ + Vector connectors=new Vector(); + + boolean running=false; + + static final Log mylog=LogFactory.getLog(ConnectorTable.class); + static final boolean mywarn=mylog.isWarnEnabled(); + + + + + public ConnectorTable(InetSocketAddress mcast_addr, + int receive_buffer_size, int receive_sock_buf_size, + boolean ip_mcast, Receiver receiver) throws IOException { + this.receiver=receiver; + this.mcastAddr=mcast_addr; + this.receive_buffer=new byte[receive_buffer_size]; + + if(ip_mcast) { + mcast_sock=new MulticastSocket(mcast_addr.getPort()); + // changed Bela Dec 31 2003: if loopback is disabled other members on the same machine won't be able + // to receive our multicasts + // mcast_sock.setLoopbackMode(true); // do not get own multicasts + mcast_sock.setReceiveBufferSize(receive_sock_buf_size); + } + } + + + public Receiver getReceiver() { + return receiver; + } + + public void setReceiver(Receiver receiver) { + this.receiver=receiver; + } + + + /** Get all interfaces, create one Connector per interface and call start() on it */ + public void start() throws Exception { + Connector tmp; + if(running) + return; + + if(mcast_sock != null) { + // Start the thread servicing the incoming multicasts + t=new Thread(this, "ConnectorTable thread"); + t.setDaemon(true); + t.start(); + } + + + // Start all Connectors + for(Iterator it=connectors.iterator(); it.hasNext();) { + tmp=(Connector)it.next(); + tmp.start(); + } + + running=true; + } + + + public void stop() { + Connector tmp; + for(Iterator it=connectors.iterator(); it.hasNext();) { + tmp=(Connector)it.next(); + tmp.stop(); + } + connectors.clear(); + t=null; + if(mcast_sock != null) { + mcast_sock.close(); + mcast_sock=null; + } + running=false; + } + + + public void run() { + // receive mcast packets on any interface of the list of interfaces we're listening on + DatagramPacket p=new DatagramPacket(receive_buffer, receive_buffer.length); + while(t != null && mcast_sock != null && !mcast_sock.isClosed()) { + p.setData(receive_buffer, 0, receive_buffer.length); + try { + receivePacket(p, mcast_sock, this); + } + catch(Throwable th) { + if(th == null || mcast_sock == null || mcast_sock.isClosed()) + break; + if(mylog.isErrorEnabled()) mylog.error("exception=" + th); + Util.sleep(300); // so we don't get into 100% cpu spinning (should NEVER happen !) + } + } + t=null; + } + + + /** + * Returns a list of local addresses (one for each Connector) + * @return List + */ + public List getConnectorAddresses() { + Connector c; + ArrayList ret=new ArrayList(); + for(Iterator it=connectors.iterator(); it.hasNext();) { + c=(Connector)it.next(); + ret.add(c.getLocalAddress()); + } + return ret; + } + + /** Sends a packet. If the destination is a multicast address, call send() on all connectors. + * If destination is not null, send the message using any Connector: if we send a unicast + * message, it doesn't matter to which interface we are bound; the kernel will choose the correct + * interface based on the destination and the routing table. Note that the receiver will have the + * interface which was chosen by the kernel to send the message as the receiver's address, so the + * correct Connector will receive a possible response. + * @param msg + * @throws Exception + */ + public void send(DatagramPacket msg) throws Exception { + InetAddress dest; + + if(msg == null) + return; + dest=msg.getAddress(); + if(dest == null) + throw new IOException("UDP_NIO.ConnectorTable.send(): destination address is null"); + + if(dest.isMulticastAddress()) { + // send to all Connectors + for(int i=0; i < connectors.size(); i++) { + ((Connector)connectors.get(i)).send(msg); + } + } + else { + // send to a random connector + Connector c=pickRandomConnector(connectors); + c.send(msg); + } + } + + private Connector pickRandomConnector(Vector conns) { + int size=conns.size(); + int index=((int)(Util.random(size))) -1; + return (Connector)conns.get(index); + } + + /** + * Adds the given interface address to the list of interfaces on which the receiver mcast + * socket has to listen. + * Also creates a new Connector. Calling this method twice on the same interface will throw an exception + * @param bind_interface + * @param local_port + * @param port_range + * @param receive_buffer_size + * @throws IOException + */ + public void listenOn(String bind_interface, int local_port, int port_range, + int receive_buffer_size, int receiver_sock_buf_size, int send_sock_buf_size, + int ip_ttl, Receiver receiver) throws IOException { + if(bind_interface == null) + return; + + NetworkInterface ni=NetworkInterface.getByInetAddress(InetAddress.getByName(bind_interface)); + if(ni == null) + throw new IOException("UDP_NIO.ConnectorTable.listenOn(): bind interface for " + + bind_interface + " not found"); + + Connector tmp=findConnector(ni); + if(tmp != null) { + if(mywarn) mylog.warn("connector for interface " + bind_interface + + " is already present (will be skipped): " + tmp); + return; + } + + // 1. join the group on this interface + if(mcast_sock != null) { + mcast_sock.joinGroup(mcastAddr, ni); + + if(mylog.isInfoEnabled()) mylog.info("joining " + mcastAddr + " on interface " + ni); + } + + // 2. create a new Connector + tmp=new Connector(ni, local_port, port_range, receive_buffer_size, receiver_sock_buf_size, + send_sock_buf_size, ip_ttl, receiver); + connectors.add(tmp); + } + + private Connector findConnector(NetworkInterface ni) { + for(int i=0; i < connectors.size(); i++) { + Connector c=(Connector)connectors.elementAt(i); + if(c.getBindInterface().equals(ni)) + return c; + } + return null; + } + + + public void receive(DatagramPacket packet) { + if(receiver != null) { + receiver.receive(packet); + } + } + + + public String toString() { + StringBuilder sb=new StringBuilder(); + sb.append("*** todo: implement ***"); + return sb.toString(); + } + + + public static void receivePacket(DatagramPacket packet, DatagramSocket sock, Receiver receiver) throws IOException { + int len; + + sock.receive(packet); + len=packet.getLength(); + if(len == 1 && packet.getData()[0] == 0) { + if(mylog.isTraceEnabled()) mylog.trace("received dummy packet"); + return; + } + if(receiver != null) + receiver.receive(packet); + } + } + + + + + + + public static class MyReceiver implements Receiver { + ConnectorTable t=null; + + public MyReceiver() { + + } + + public void setConnectorTable(ConnectorTable t) { + this.t=t; + } + + public void receive(DatagramPacket packet) { + System.out.println("-- received " + packet.getLength() + " bytes from " + packet.getSocketAddress()); + InetAddress sender=packet.getAddress(); + byte[] buf=packet.getData(); + int len=packet.getLength(); + String tmp=new String(buf, 0, len); + if(len > 4) { + if(tmp.startsWith("rsp:")) { + System.out.println("-- received respose: \"" + tmp + '\"'); + return; + } + } + + byte[] rsp_buf=("rsp: this is a response to " + tmp).getBytes(); + DatagramPacket response=new DatagramPacket(rsp_buf, rsp_buf.length, sender, packet.getPort()); + + try { + t.send(response); + } + catch(Exception e) { + e.printStackTrace(); + System.err.println("MyReceiver: problem sending response to " + sender); + } + } + } + + + + public static class MulticastReceiver implements Runnable { + Unmarshaller m=null; + DatagramSocket sock=null; // may be DatagramSocket or MulticastSocket + + public void run() { + // receives packet from socket + // calls Unmarshaller.receive() + } + + } + + public static class Unmarshaller { + Queue q=null; + + void receive(byte[] data, SocketAddress sender) { + // if (q) --> q.add() + // unserialize and call handleMessage() + } + } + + + + static void help() { + System.out.println("UDP_NIO [-help] [-bind_addrs ]"); + } + + + + public static void main(String[] args) { + MyReceiver r=new MyReceiver(); + ConnectorTable ct; + String line; + InetSocketAddress mcast_addr; + BufferedReader in=null; + DatagramPacket packet; + byte[] send_buf; + int receive_buffer_size=65000; + boolean ip_mcast=true; + + try { + mcast_addr=new InetSocketAddress("230.1.2.3", 7500); + ct=new ConnectorTable(mcast_addr, receive_buffer_size, 120000, ip_mcast, r); + r.setConnectorTable(ct); + } + catch(Throwable t) { + t.printStackTrace(); + return; + } + + for(int i=0; i < args.length; i++) { + if("-help".equals(args[i])) { + help(); + continue; + } + if("-bind_addrs".equals(args[i])) { + while(++i < args.length && !args[i].trim().startsWith("-")) { + try { + ct.listenOn(args[i], 0, 1, receive_buffer_size, 120000, 12000, 32, r); + } + catch(IOException e) { + e.printStackTrace(); + return; + } + } + } + } + + + try { + ct.start(); // starts all Connectors in turn + in=new BufferedReader(new InputStreamReader(System.in)); + while(true) { + System.out.print("> "); System.out.flush(); + line=in.readLine(); + if(line.startsWith("quit") || line.startsWith("exit")) + break; + send_buf=line.getBytes(); + packet=new DatagramPacket(send_buf, send_buf.length, mcast_addr); + ct.send(packet); + } + } + catch(Exception e) { + e.printStackTrace(); + } + finally { + if(ct != null) + ct.stop(); + } + } + + + + +} + + +interface Receiver { + + /** Called when data has been received on a socket. When the callback returns, the buffer will be + * reused: therefore, if buf must be processed on a separate thread, it needs to be copied. + * This method might be called concurrently by multiple threads, so it has to be reentrant + * @param packet + */ + void receive(DatagramPacket packet); +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/obsolete/UNIFORM.java.txt =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/obsolete/UNIFORM.java.txt,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/obsolete/UNIFORM.java.txt 17 Aug 2012 14:51:27 -0000 1.1 @@ -0,0 +1,349 @@ +// $Id: UNIFORM.java.txt,v 1.1 2012/08/17 14:51:27 marcin Exp $ + +package org.jgroups.protocols; + +import java.io.Serializable; +import java.util.Hashtable; +import java.util.Properties; +import java.util.Vector; + + + +class UniformHeader implements Serializable { + public static final int SAVE = 0; + public static final int SAVE_OK = 1; + public static final int DELIVER = 2; + public static final int DELIVER_OK = 3; + public static final int SEEN = 4; + public static final int SEEN_OK = 5; + public static final int SEEN_NOTOK = 6; + public static final int GC = 7; + public static final int GC_OK = 8; + + public int type=-1; + public long id=-1; + public boolean handle=true; + + + String type2Str(int t) { + switch(t) { + case SAVE: return "SAVE"; + case SAVE_OK: return "SAVE_OK"; + case DELIVER: return "DELIVER"; + case DELIVER_OK: return "DELIVER_OK"; + case SEEN: return "SEEN"; + case SEEN_OK: return "SEEN_OK"; + case GC: return "GC"; + case GC_OK: return "GC_OK"; + default: return ""; + } + } + + + public UniformHeader() {handle=false;} + + + public UniformHeader(int type) { + this.type=type; + id=System.currentTimeMillis(); + handle=true; + } + + + public UniformHeader(int type, long id) { + this.type=type; + this.id=id; + handle=true; + } + + + public String toString() {return "[UNIFORM: type=" + type2Str(type) + ", id=" + id + "]";} +} + + + + + +/** + The algorithms implements dynamically-uniform failure-atomic group multicast, + that is, a message is delivered by all members if it is delivered by at least 1 + non-faulty member even if the sender crashes after sending. If the sender crashes, it + will eventually be removed from the group membership: the FLUSH protocol preceding the + view change causes all pending multicasts to be flushed out of the system, thereby + re-sending pending multicasts to members that haven't received them yet.

        + The protocol makes use of GroupRequest (which itself uses + RequestCorrelator) to send a request to all members and receive responses from + all non-faulty members. + */ + +public class UNIFORM extends Protocol implements RequestHandler, Transport { + Vector members=null; + boolean trace=false; + RequestCorrelator corr=new RequestCorrelator(getName(), this, this); + Hashtable pending=new Hashtable(); // key = sender, val = Hashtable (msg-id, msg) + Hashtable delivered=new Hashtable(); // key = sender, val = Hashtable (msg-id, msg) + + + public String getName() {return "UNIFORM";} + + + public boolean setProperties(Properties props) {super.setProperties(props); + String str; + + this.props=props; + str=props.getProperty("trace"); + if(str != null) { + trace=new Boolean(str).booleanValue(); + props.remove("trace"); + } + if(props.size() > 0) { + log.error("UNIFORM.setProperties(): the following properties are not recognized: " + props); + + return false; + } + return true; + } + + + /** Just remove if you don't need to reset any state */ + public void reset() {} + + + + + public void up(Event evt) { + Message msg; + boolean rc; + Object obj; + + if(evt.getType() == Event.START) { + corr.start(); + passUp(evt); + return; + } + corr.receive(evt); + } + + + + + public void down(Event evt) { + Message msg; + GroupRequest save_req, deliver_req, seen_req, gc_req; + AndCommand and_comm; + Message save_msg, deliver_msg, seen_msg, gc_msg; + Vector mbrs=null; + long id=0; + + switch(evt.getType()) { + case Event.STOP: + corr.Stop(); + passDown(evt); + break; + + case Event.TMP_VIEW: + case Event.VIEW_CHANGE: + Vector tmp; + if((tmp=(Vector)((View)evt.getArg()).getMembers()) != null) + members=tmp; + passDown(evt); + break; + + case Event.MSG: + msg=(Message)evt.getArg(); + + + if(msg.getDest() != null) { // unicast msg + passDown(evt); + return; + } + + id=System.currentTimeMillis(); + mbrs=(Vector)members.clone(); + + + + /* + 1. Create 4 commands (SaveCommand, OkCommand, SeenCommand and GcCommand). + Each has the same unique ID, and each is tagged with its type + (e.g. SAVE_REQ, OK_REQ etc). ID and type are contained in a UniformHeader + attached to the message (with each command). + 2. Create an AndCommand and add the 4 commands. + 3. Add the AndCommand to a list of currently running commands and execute it. + 4. When a FLUSH request is received, wait until all commands are done. + */ + + save_msg=msg; + save_msg.addHeader(new UniformHeader(UniformHeader.SAVE, id)); + save_req=new GroupRequest(save_msg, corr, mbrs, GroupRequest.GET_ALL); + + deliver_msg=new Message(null, null, null); + deliver_msg.addHeader(new UniformHeader(UniformHeader.DELIVER, id)); + deliver_req=new GroupRequest(deliver_msg, corr, mbrs, GroupRequest.GET_ALL); + + seen_msg=new Message(null, null, null); + seen_msg.addHeader(new UniformHeader(UniformHeader.SEEN, id)); + seen_req=new GroupRequest(seen_msg, corr, mbrs, GroupRequest.GET_ALL); + + gc_msg=new Message(null, null, null); + gc_msg.addHeader(new UniformHeader(UniformHeader.GC, id)); + gc_req=new GroupRequest(gc_msg, corr, mbrs, GroupRequest.GET_ALL); + + and_comm=new AndCommand(); + and_comm.add(save_req); + and_comm.add(deliver_req); + and_comm.add(seen_req); + and_comm.add(gc_req); + + boolean rc=and_comm.execute(); + System.out.println("UNIFORM: rc from Execute is " + rc); + + + break; + + default: + passDown(evt); // Pass on to the layer below us + } + + } + + + + + /** +

        +       1. Remove UniformHeader from message and get ID and type
        +       2. If type == SAVE: add message to save-table (key=sender + ID) and send response (SAVE_OK)
        +          If type == OK:   add message to ok-table, remove from save-table. Deliver message (pass up
        +	  the stack) and send response (OK_OK).
        +	  If type == SEEN: find message in ok-table. If found, send SEEN_OK response, else NOT_SEEN.
        +	  If type == GC: delete message from ok-table.
        +       
        + */ + public Object handle(Message msg) { + UniformHeader hdr; + Object obj=msg.peekHeader(); + Message m=null; + + if(obj != null && obj instanceof UniformHeader) { + hdr=(UniformHeader)msg.removeHeader(); + + switch(hdr.type) { + case UniformHeader.SAVE: + + System.out.println("==> save in pending: " + msg.getSrc() + ":" + hdr.id); + + saveInPending(hdr.id, msg); + return new Integer(UniformHeader.SAVE_OK); + case UniformHeader.DELIVER: + + System.out.println("==> move to delivered: " + msg.getSrc() + ":" + hdr.id); + + m=moveFromPendingToDelivered(msg.getSrc(), hdr.id); + if(m != null) + passUp(new Event(Event.MSG, m)); + return new Integer(UniformHeader.DELIVER_OK); + case UniformHeader.SEEN: + + System.out.print("==> find in delivered: " + msg.getSrc() + ":" + hdr.id); + + if(findInDelivered(msg.getSrc(), hdr.id)) { + System.out.println(" SEEN_OK"); + return new Integer(UniformHeader.SEEN_OK); + } + + System.out.println(" SEEN_NOTOK"); + return new Integer(UniformHeader.SEEN_NOTOK); + + case UniformHeader.GC: + + System.out.println("==> remove from delivered: " + msg.getSrc() + ":" + hdr.id); + + removeFromDelivered(msg.getSrc(), hdr.id); + return new Integer(UniformHeader.GC_OK); + default: + log.error("UNIFORM.handle(): UniformHeader.type " + hdr.type + " not known"); + break; + } + } + + return null; + } + + + + /* --------------------------- Transport interface ---------------------------------- */ + public void send(Message msg) throws Exception {passDown(new Event(Event.MSG, msg));} + public Object receive(long timeout) throws Exception {return null;} + /* ------------------------ End of Transport interface ------------------------------ */ + + + + void saveInPending(long msg_id, Message msg) { + Object sender=msg.getSrc(); + Long key=new Long(msg_id); + Hashtable val=(Hashtable)pending.get(sender); // look for sender as key + + if(val == null) { + val=new Hashtable(); + pending.put(sender, val); + } + if(!val.containsKey(key)) + val.put(key, msg); + } + + + + Message moveFromPendingToDelivered(Object sender, long msg_id) { + Message msg=null; + Hashtable val_pending, val_delivered; + Long key=new Long(msg_id); + + val_pending=(Hashtable)pending.get(sender); + if(val_pending == null) { + log.error("UNIFORM.moveFromPendingToDelivered(): value for " + + sender + " not found !"); + return null; + } + msg=(Message)val_pending.get(key); + if(msg == null) { + log.error("UNIFORM.moveFromPendingToDelivered(): value for " + sender + ":" + + key + " not found !"); + return null; + } + + val_delivered=(Hashtable)delivered.get(sender); + if(val_delivered == null) { + val_delivered=new Hashtable(); + delivered.put(sender, val_delivered); + } + if(!val_delivered.containsKey(key)) + val_delivered.put(key, msg); + val_pending.remove(key); // remove from pending table + if(val_pending.size() == 0) + pending.remove(sender); + + return msg; + } + + + + + boolean findInDelivered(Object sender, long msg_id) { + Hashtable val=(Hashtable)delivered.get(sender); + if(val == null) + return false; + return val.containsKey(new Long(msg_id)); + } + + + + void removeFromDelivered(Object sender, long msg_id) { + Hashtable val=(Hashtable)delivered.get(sender); + if(val == null) + return; + val.remove(new Long(msg_id)); + if(val.size() == 0) + delivered.remove(sender); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/ClientGmsImpl.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/ClientGmsImpl.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/ClientGmsImpl.java 17 Aug 2012 14:51:23 -0000 1.1 @@ -0,0 +1,366 @@ + +package org.jgroups.protocols.pbcast; + + +import org.jgroups.*; +import org.jgroups.protocols.PingRsp; +import org.jgroups.util.Promise; +import org.jgroups.util.Util; +import org.jgroups.util.Digest; +import org.jgroups.util.MutableDigest; + +import java.util.*; + + +/** + * Client part of GMS. Whenever a new member wants to join a group, it starts in the CLIENT role. + * No multicasts to the group will be received and processed until the member has been joined and + * turned into a SERVER (either coordinator or participant, mostly just participant). This class + * only implements Join (called by clients who want to join a certain group, and + * ViewChange which is called by the coordinator that was contacted by this client, to + * tell the client what its initial membership is. + * @author Bela Ban + * @version $Id: ClientGmsImpl.java,v 1.1 2012/08/17 14:51:23 marcin Exp $ + */ +public class ClientGmsImpl extends GmsImpl { + private final Promise join_promise=new Promise(); + + + public ClientGmsImpl(GMS g) { + super(g); + } + + public void init() throws Exception { + super.init(); + join_promise.reset(); + } + + public void join(Address address) { + join(address, false); + } + + + public void joinWithStateTransfer(Address address) { + join(address, true); + } + + /** + * Joins this process to a group. Determines the coordinator and sends a + * unicast handleJoin() message to it. The coordinator returns a JoinRsp and + * then broadcasts the new view, which contains a message digest and the + * current membership (including the joiner). The joiner is then supposed to + * install the new view and the digest and starts accepting mcast messages. + * Previous mcast messages were discarded (this is done in PBCAST). + *

        + * If successful, impl is changed to an instance of ParticipantGmsImpl. + * Otherwise, we continue trying to send join() messages to the coordinator, + * until we succeed (or there is no member in the group. In this case, we + * create our own singleton group). + *

        + * When GMS.disable_initial_coord is set to true, then we won't become + * coordinator on receiving an initial membership of 0, but instead will + * retry (forever) until we get an initial membership of > 0. + * + * @param mbr Our own address (assigned through SET_LOCAL_ADDRESS) + */ + private void join(Address mbr, boolean joinWithStateTransfer) { + Address coord=null; + JoinRsp rsp=null; + View tmp_view; + leaving=false; + + join_promise.reset(); + while(!leaving) { + if(rsp == null && !join_promise.hasResult()) { // null responses means that the discovery was cancelled + List responses=findInitialMembers(join_promise); + if(log.isDebugEnabled()) + log.debug("initial_mbrs are " + responses); + if(responses == null || responses.isEmpty()) { + if(gms.disable_initial_coord) { + if(log.isTraceEnabled()) + log.trace("received an initial membership of 0, but cannot become coordinator " + "(disable_initial_coord=true), will retry fetching the initial membership"); + continue; + } + if(log.isDebugEnabled()) + log.debug("no initial members discovered: creating group as first member"); + becomeSingletonMember(mbr); + return; + } + + coord=determineCoord(responses); + if(coord == null) { // e.g. because we have all clients only + if(gms.handle_concurrent_startup == false) { + if(log.isTraceEnabled()) + log.trace("handle_concurrent_startup is false; ignoring responses of initial clients"); + becomeSingletonMember(mbr); + return; + } + + if(log.isTraceEnabled()) + log.trace("could not determine coordinator from responses " + responses); + + // so the member to become singleton member (and thus coord) is the first of all clients + Set

        clients=new TreeSet
        (); // sorted + clients.add(mbr); // add myself again (was removed by findInitialMembers()) + for(PingRsp response: responses) { + Address client_addr=response.getAddress(); + if(client_addr != null) + clients.add(client_addr); + } + if(log.isTraceEnabled()) + log.trace("clients to choose new coord from are: " + clients); + Address new_coord=clients.iterator().next(); + if(new_coord.equals(mbr)) { + if(log.isTraceEnabled()) + log.trace("I (" + mbr + ") am the first of the clients, will become coordinator"); + becomeSingletonMember(mbr); + return; + } + else { + if(log.isTraceEnabled()) + log.trace("I (" + mbr + + ") am not the first of the clients, waiting for another client to become coordinator"); + Util.sleep(500); + } + continue; + } + + if(log.isDebugEnabled()) + log.debug("sending handleJoin(" + mbr + ") to " + coord); + sendJoinMessage(coord, mbr, joinWithStateTransfer); + } + + try { + if(rsp == null) + rsp=join_promise.getResult(gms.join_timeout); + if(rsp == null) { + if(log.isWarnEnabled()) + log.warn("join(" + mbr + ") sent to " + coord + " timed out (after " + gms.join_timeout + " ms), retrying"); + } + else { + // 1. check whether JOIN was rejected + String failure=rsp.getFailReason(); + if(failure != null) + throw new SecurityException(failure); + + // 2. Install digest + // See if the digest does not have senders, this seems to happen on high volume startup + if(rsp.getDigest() == null || rsp.getDigest().getSenders() == null) { + if(log.isWarnEnabled()) + log.warn("digest response has no senders: digest=" + rsp.getDigest()); + rsp=null; // just skip the response we guess + continue; + } + MutableDigest tmp_digest=new MutableDigest(rsp.getDigest()); + tmp_view=rsp.getView(); + if(tmp_view == null) { + if(log.isErrorEnabled()) + log.error("JoinRsp has a null view, skipping it"); + rsp=null; + } + else { + if(!tmp_digest.contains(gms.local_addr)) { + throw new IllegalStateException("digest returned from " + coord + " with JOIN_RSP does not contain myself (" + + gms.local_addr + "): join response: " + rsp); + } + tmp_digest.incrementHighestDeliveredSeqno(coord); // see DESIGN for details + tmp_digest.seal(); + gms.setDigest(tmp_digest); + + if(log.isDebugEnabled()) + log.debug("[" + gms.local_addr + "]: JoinRsp=" + tmp_view + " [size=" + tmp_view.size() + "]\n\n"); + + if(!installView(tmp_view)) { + if(log.isErrorEnabled()) + log.error("view installation failed, retrying to join group"); + rsp=null; + continue; + } + + // send VIEW_ACK to sender of view + Message view_ack=new Message(coord, null, null); + view_ack.setFlag(Message.OOB); + GMS.GmsHeader tmphdr=new GMS.GmsHeader(GMS.GmsHeader.VIEW_ACK); + view_ack.putHeader(GMS.name, tmphdr); + if(!gms.members.contains(coord)) + gms.getDownProtocol().down(new Event(Event.ENABLE_UNICASTS_TO, coord)); + gms.getDownProtocol().down(new Event(Event.MSG, view_ack)); + return; + } + } + } + catch(SecurityException security_ex) { + throw security_ex; + } + catch(IllegalArgumentException illegal_arg) { + throw illegal_arg; + } + catch(Throwable e) { + if(log.isDebugEnabled()) + log.debug("exception=" + e + ", retrying"); + rsp=null; + } + } + } + + private List findInitialMembers(Promise promise) { + List responses=(List)gms.getDownProtocol().down(new Event(Event.FIND_INITIAL_MBRS, promise)); + if(responses != null) { + for(Iterator iter=responses.iterator(); iter.hasNext();) { + PingRsp response=iter.next(); + if(response.own_addr != null && response.own_addr.equals(gms.local_addr)) + iter.remove(); + } + } + return responses; + } + + public void leave(Address mbr) { + leaving=true; + wrongMethod("leave"); + } + + + public void handleJoinResponse(JoinRsp join_rsp) { + join_promise.setResult(join_rsp); // will wake up join() method + } + + public void handleLeaveResponse() { + } + + + public void suspect(Address mbr) { + } + + public void unsuspect(Address mbr) { + } + + public void handleMembershipChange (Collection requests) { + } + + + /** + * Does nothing. Discards all views while still client. + */ + public synchronized void handleViewChange(View new_view, Digest digest) { + if(log.isTraceEnabled()) + log.trace("view " + new_view.getVid() + " is discarded as we are not a participant"); + } + + + /** + * Called by join(). Installs the view returned by calling Coord.handleJoin() and + * becomes coordinator. + */ + private boolean installView(View new_view) { + Vector
        mems=new_view.getMembers(); + if(log.isDebugEnabled()) log.debug("new_view=" + new_view); + if(gms.local_addr == null || mems == null || !mems.contains(gms.local_addr)) { + if(log.isErrorEnabled()) + log.error("I (" + gms.local_addr + ") am not member of " + mems + ", will not install view"); + return false; + } + gms.installView(new_view); + gms.becomeParticipant(); + gms.getUpProtocol().up(new Event(Event.BECOME_SERVER)); + gms.getDownProtocol().down(new Event(Event.BECOME_SERVER)); + return true; + } + + + /** Returns immediately. Clients don't handle suspect() requests */ + // public void handleSuspect(Address mbr) { + // } + + + /* --------------------------- Private Methods ------------------------------------ */ + + + + void sendJoinMessage(Address coord, Address mbr,boolean joinWithTransfer) { + Message msg; + GMS.GmsHeader hdr; + + msg=new Message(coord, null, null); + msg.setFlag(Message.OOB); + if(joinWithTransfer) + hdr=new GMS.GmsHeader(GMS.GmsHeader.JOIN_REQ_WITH_STATE_TRANSFER, mbr); + else + hdr=new GMS.GmsHeader(GMS.GmsHeader.JOIN_REQ, mbr); + msg.putHeader(gms.getName(), hdr); + + // we have to enable unicasts to coord, as coord is not in our membership (the unicast message would get dropped) + gms.getDownProtocol().down(new Event(Event.ENABLE_UNICASTS_TO, coord)); + gms.getDownProtocol().down(new Event(Event.MSG, msg)); + } + + /** + The coordinator is determined by a majority vote. If there are an equal number of votes for + more than 1 candidate, we determine the winner randomly. + */ + private Address determineCoord(List mbrs) { + int count, most_votes; + Address winner=null, tmp; + + if(mbrs == null || mbrs.size() < 1) + return null; + + Map votes=new HashMap(5); + + // count *all* the votes (unlike the 2000 election) + for(PingRsp mbr:mbrs) { + if(mbr.is_server && mbr.coord_addr != null) { + if(!votes.containsKey(mbr.coord_addr)) + votes.put(mbr.coord_addr, 1); + else { + count=votes.get(mbr.coord_addr); + votes.put(mbr.coord_addr, count + 1); + } + } + } + + if(votes.size() > 1) { + if(log.isWarnEnabled()) log.warn("there was more than 1 candidate for coordinator: " + votes); + } + else { + if(log.isDebugEnabled()) log.debug("election results: " + votes); + } + + // determine who got the most votes + most_votes=0; + for(Map.Entry entry: votes.entrySet()) { + tmp=entry.getKey(); + count=entry.getValue(); + if(count > most_votes) { + winner=tmp; + // fixed July 15 2003 (patch submitted by Darren Hobbs, patch-id=771418) + most_votes=count; + } + } + votes.clear(); + return winner; + } + + + void becomeSingletonMember(Address mbr) { + Digest initial_digest; + ViewId view_id; + Vector
        mbrs=new Vector
        (1); + + // set the initial digest (since I'm the first member) + initial_digest=new Digest(gms.local_addr, 0, 0); // initial seqno mcast by me will be 1 (highest seen +1) + gms.setDigest(initial_digest); + + view_id=new ViewId(mbr); // create singleton view with mbr as only member + mbrs.addElement(mbr); + gms.installView(new View(view_id, mbrs)); + gms.becomeCoordinator(); // not really necessary - installView() should do it + + gms.getUpProtocol().up(new Event(Event.BECOME_SERVER)); + gms.getDownProtocol().down(new Event(Event.BECOME_SERVER)); + if(log.isDebugEnabled()) log.debug("created group (first member). My view is " + gms.view_id + + ", impl is " + gms.getImpl().getClass().getName()); + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/CoordGmsImpl.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/CoordGmsImpl.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/CoordGmsImpl.java 17 Aug 2012 14:51:23 -0000 1.1 @@ -0,0 +1,902 @@ +// $Id: CoordGmsImpl.java,v 1.1 2012/08/17 14:51:23 marcin Exp $ + +package org.jgroups.protocols.pbcast; + + +import org.jgroups.*; +import org.jgroups.annotations.GuardedBy; +import org.jgroups.util.Digest; +import org.jgroups.util.MutableDigest; + +import java.util.*; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + + +/** + * Coordinator role of the Group MemberShip (GMS) protocol. Accepts JOIN and LEAVE requests and emits view changes + * accordingly. + * @author Bela Ban + */ +public class CoordGmsImpl extends GmsImpl { + private volatile boolean merging=false; + private final MergeTask merge_task=new MergeTask(); + private final Vector merge_rsps=new Vector(11); + // for MERGE_REQ/MERGE_RSP correlation, contains MergeData elements + private ViewId merge_id=null; + + @GuardedBy("merge_canceller_lock") + private Future merge_canceller_future=null; + + private final Lock merge_canceller_lock=new ReentrantLock(); + + /** the max time in ms to suspend message garbage collection */ + private final Long MAX_SUSPEND_TIMEOUT=new Long(30000); + + + public CoordGmsImpl(GMS g) { + super(g); + } + + + private void setMergeId(ViewId merge_id) { + this.merge_id=merge_id; + if(this.merge_id != null) { + stopMergeCanceller(); + startMergeCanceller(); + } + else { // merge completed + stopMergeCanceller(); + } + } + + private void startMergeCanceller() { + merge_canceller_lock.lock(); + try { + if(merge_canceller_future == null || merge_canceller_future.isDone()) { + MergeCanceller task=new MergeCanceller(this.merge_id, gms.merge_timeout); + merge_canceller_future=gms.timer.schedule(task, gms.merge_timeout, TimeUnit.MILLISECONDS); + } + } + finally { + merge_canceller_lock.unlock(); + } + } + + private void stopMergeCanceller() { + merge_canceller_lock.lock(); + try { + if(merge_canceller_future != null) { + merge_canceller_future.cancel(true); + merge_canceller_future=null; + } + } + finally { + merge_canceller_lock.unlock(); + } + } + + public void init() throws Exception { + super.init(); + cancelMerge(); + } + + public void join(Address mbr) { + wrongMethod("join"); + } + + public void joinWithStateTransfer(Address mbr) { + wrongMethod("join"); + } + + /** The coordinator itself wants to leave the group */ + public void leave(Address mbr) { + if(mbr == null) { + if(log.isErrorEnabled()) log.error("member's address is null !"); + return; + } + if(mbr.equals(gms.local_addr)) + leaving=true; + gms.getViewHandler().add(new Request(Request.LEAVE, mbr, false, null)); + gms.getViewHandler().stop(true); // wait until all requests have been processed, then close the queue and leave + gms.getViewHandler().waitUntilCompleted(gms.leave_timeout); + } + + public void handleJoinResponse(JoinRsp join_rsp) { + } + + public void handleLeaveResponse() { + } + + public void suspect(Address mbr) { + if(mbr.equals(gms.local_addr)) { + if(log.isWarnEnabled()) log.warn("I am the coord and I'm being am suspected -- will probably leave shortly"); + return; + } + Collection suspected=new LinkedHashSet(1); + suspected.add(new Request(Request.SUSPECT,mbr,true,null)); + handleMembershipChange(suspected); + } + + public void unsuspect(Address mbr) { + + } + + /** + * Invoked upon receiving a MERGE event from the MERGE layer. Starts the merge protocol. + * See description of protocol in DESIGN. + * @param other_coords A list of coordinators (including myself) found by MERGE protocol + */ + public void merge(Vector
        other_coords) { + Membership tmp; + + if(merging) { + if(log.isWarnEnabled()) log.warn("merge already in progress, discarded MERGE event (I am " + gms.local_addr + ")"); + return; + } + if(other_coords == null) { + if(log.isWarnEnabled()) log.warn("list of other coordinators is null. Will not start merge."); + return; + } + + if(other_coords.size() <= 1) { + if(log.isErrorEnabled()) + log.error("number of coordinators found is " + other_coords.size() + "; will not perform merge"); + return; + } + + /* Establish deterministic order, so that coords can elect leader */ + tmp=new Membership(other_coords); + tmp.sort(); + Address merge_leader=tmp.elementAt(0); + if(log.isDebugEnabled()) log.debug("Determining merge leader from coordinators: " + tmp); + if(merge_leader.equals(gms.local_addr) || gms.merge_leader) { + if(log.isDebugEnabled()) + log.debug("I (" + gms.local_addr + ") will be the leader. Starting the merge task for " + other_coords); + startMergeTask(other_coords); + } + else { + if(log.isDebugEnabled()) log.debug("I (" + gms.local_addr + ") am not the merge leader, " + + "waiting for merge leader (" + merge_leader + ") to initiate merge"); + } + } + + /** + * Get the view and digest and send back both (MergeData) in the form of a MERGE_RSP to the sender. + * If a merge is already in progress, send back a MergeData with the merge_rejected field set to true. + */ + public void handleMergeRequest(Address sender, ViewId merge_id) { + Digest digest; + View view; + + if(sender == null) { + if(log.isErrorEnabled()) log.error("sender == null; cannot send back a response"); + return; + } + if(merging) { + if(log.isErrorEnabled()) log.error("For merge participant " + gms.local_addr +" merge is already in progress"); + sendMergeRejectedResponse(sender, merge_id); + return; + } + merging=true; + + if(log.isDebugEnabled()) log.debug("Suspending view handler at " + gms.local_addr); + /* Clears the view handler queue and discards all JOIN/LEAVE/MERGE requests until after the MERGE */ + gms.getViewHandler().suspend(merge_id); + + setMergeId(merge_id); + if(log.isDebugEnabled()) log.debug(gms.local_addr + " got merge request from " + sender + ", merge_id=" + merge_id); + view=new View(gms.view_id.copy(), gms.members.getMembers()); + + //[JGRP-524] - FLUSH and merge: flush doesn't wrap entire merge process + //[JGRP-770] - Concurrent startup of many channels doesn't stabilize + //[JGRP-700] - FLUSH: flushing should span merge + + /*if flush is in stack, let this coordinator flush its cluster island */ + boolean suceesfulFlush = gms.startFlush(view); + if(suceesfulFlush) { + digest=gms.getDigest(); + sendMergeResponse(sender, view, digest); + if(log.isDebugEnabled()) + log.debug(gms.local_addr + " responded to " + sender + ", merge_id=" + merge_id); + } + else { + sendMergeRejectedResponse(sender, merge_id); + gms.getViewHandler().resume(merge_id); + merging=false; + if(log.isWarnEnabled()) + log.warn("Since flush failed at " + gms.local_addr + " rejected merge to "+ sender+ ", merge_id="+ merge_id); + } + } + + public void handleMergeResponse(MergeData data, ViewId merge_id) { + if(merge_id == null || this.merge_id == null) { + if(log.isErrorEnabled()) + log.error("merge_id (" + + merge_id + + ") or this.merge_id (" + + this.merge_id + + ") is null (sender=" + + data.getSender() + + ")."); + return; + } + + if(!this.merge_id.equals(merge_id)) { + if(log.isErrorEnabled()) log.error("this.merge_id (" + + this.merge_id + + ") is different from merge_id (" + + merge_id + + ')'); + return; + } + + synchronized(merge_rsps) { + if(!merge_rsps.contains(data)) { + merge_rsps.addElement(data); + merge_rsps.notifyAll(); + } + } + } + + /** + * If merge_id is not equal to this.merge_id then discard. + * Else cast the view/digest to all members of this group. + */ + public void handleMergeView(final MergeData data,final ViewId merge_id) { + if(merge_id == null + || this.merge_id == null + || !this.merge_id.equals(merge_id)) { + if(log.isErrorEnabled()) log.error("merge_ids don't match (or are null); merge view discarded"); + return; + } + + // only send to our *current* members, if we have A and B being merged (we are B), then we would *not* + // receive a VIEW_ACK from A because A doesn't see us in the pre-merge view yet and discards the view + + //[JGRP-700] - FLUSH: flushing should span merge + + //we have to send new view only to current members and we should not wait + //for view acks from newly merged mebers + List
        newViewMembers=new Vector
        (data.view.getMembers()); + newViewMembers.removeAll(gms.members.getMembers()); + + + gms.castViewChangeWithDest(data.view, data.digest, null, newViewMembers); + /* + * if we have flush in stack send ack back to merge coordinator + * */ + if(gms.flushProtocolInStack) { + Message ack=new Message(data.getSender(), null, null); + ack.setFlag(Message.OOB); + GMS.GmsHeader ack_hdr=new GMS.GmsHeader(GMS.GmsHeader.INSTALL_MERGE_VIEW_OK); + ack.putHeader(gms.getName(), ack_hdr); + gms.getDownProtocol().down(new Event(Event.ENABLE_UNICASTS_TO, data.getSender())); + gms.getDownProtocol().down(new Event(Event.MSG, ack)); + } + merging=false; + gms.getViewHandler().resume(merge_id); + } + + public void handleMergeCancelled(ViewId merge_id) { + gms.stopFlush(); + if(merge_id != null && this.merge_id != null && this.merge_id.equals(merge_id)) { + if(log.isDebugEnabled()) + log.debug("merge was cancelled at merge participant " + gms.local_addr+ " (merge_id="+ merge_id+ ")"); + + setMergeId(null); + merging=false; + gms.getViewHandler().resume(merge_id); + } + else { + if(log.isWarnEnabled()) + log.warn("merge was supposed to be cancelled at merge participant " + gms.local_addr + + " (merge_id=" + + merge_id + + "), but it is not since merge ids do not match"); + } + } + + + private void cancelMerge() { + Object tmp=merge_id; + if(merge_id != null && log.isDebugEnabled()) log.debug("cancelling merge (merge_id=" + merge_id + ')'); + setMergeId(null); + stopMergeTask(); + merging=false; + synchronized(merge_rsps) { + merge_rsps.clear(); + } + gms.getViewHandler().resume(tmp); + } + + + public void handleMembershipChange(Collection requests) { + boolean joinAndStateTransferInitiated=false; + Collection
        new_mbrs=new LinkedHashSet
        (requests.size()); + Collection
        suspected_mbrs=new LinkedHashSet
        (requests.size()); + Collection
        leaving_mbrs=new LinkedHashSet
        (requests.size()); + + for(Request req: requests) { + switch(req.type) { + case Request.JOIN: + new_mbrs.add(req.mbr); + break; + case Request.JOIN_WITH_STATE_TRANSFER: + new_mbrs.add(req.mbr); + joinAndStateTransferInitiated=true; + break; + case Request.LEAVE: + if(req.suspected) + suspected_mbrs.add(req.mbr); + else + leaving_mbrs.add(req.mbr); + break; + case Request.SUSPECT: + suspected_mbrs.add(req.mbr); + break; + } + } + + new_mbrs.remove(gms.local_addr); // remove myself - cannot join myself (already joined) + + if(gms.view_id == null) { + // we're probably not the coord anymore (we just left ourselves), let someone else do it + // (client will retry when it doesn't get a response) + if(log.isDebugEnabled()) + log.debug("gms.view_id is null, I'm not the coordinator anymore (leaving=" + leaving + + "); the new coordinator will handle the leave request"); + return; + } + + Vector
        current_members=gms.members.getMembers(); + leaving_mbrs.retainAll(current_members); // remove all elements of leaving_mbrs which are not current members + if(suspected_mbrs.remove(gms.local_addr)) { + if(log.isWarnEnabled()) log.warn("I am the coord and I'm being suspected -- will probably leave shortly"); + } + suspected_mbrs.retainAll(current_members); // remove all elements of suspected_mbrs which are not current members + + // for the members that have already joined, return the current digest and membership + for(Iterator
        it=new_mbrs.iterator(); it.hasNext();) { + Address mbr=it.next(); + if(gms.members.contains(mbr)) { // already joined: return current digest and membership + JoinRsp join_rsp; + if(gms.reject_join_from_existing_member) { + join_rsp=new JoinRsp("member " + mbr + " is already part of the group, JOIN request is rejected"); + } + else { + if(log.isWarnEnabled()) + log.warn(mbr + " already present; returning existing view " + gms.view); + join_rsp=new JoinRsp(new View(gms.view_id, gms.members.getMembers()), gms.getDigest()); + } + gms.sendJoinResponse(join_rsp, mbr); + it.remove(); + } + } + + if(new_mbrs.isEmpty() && leaving_mbrs.isEmpty() && suspected_mbrs.isEmpty()) { + if(log.isTraceEnabled()) + log.trace("found no members to add or remove, will not create new view"); + return; + } + + View new_view=gms.getNextView(new_mbrs, leaving_mbrs, suspected_mbrs); + gms.up(new Event(Event.PREPARE_VIEW,new_view)); + gms.down(new Event(Event.PREPARE_VIEW,new_view)); + + if(log.isDebugEnabled()) + log.debug("new=" + new_mbrs + ", suspected=" + suspected_mbrs + ", leaving=" + leaving_mbrs + + ", new view: " + new_view); + + JoinRsp join_rsp=null; + boolean hasJoiningMembers=!new_mbrs.isEmpty(); + try { + boolean successfulFlush = gms.startFlush(new_view); + if(!successfulFlush && hasJoiningMembers){ + //see http://jira.jboss.org/jira/browse/JGRP-759 + //We should NOT send back a join response if the flush fails. + //The joiner should block until the previous FLUSH completed + //we still have to send potential leave responses + sendLeaveResponses(leaving_mbrs); + //but let the joining client timeout and send another join request + return; + } + + // we cannot garbage collect during joining a new member *if* we're the only member + // Example: {A}, B joins, after returning JoinRsp to B, A garbage collects messages higher than those + // in the digest returned to the client, so the client will *not* be able to ask for retransmission + // of those messages if he misses them + if(hasJoiningMembers) { + gms.getDownProtocol().down(new Event(Event.SUSPEND_STABLE, MAX_SUSPEND_TIMEOUT)); + Digest tmp=gms.getDigest(); // get existing digest + MutableDigest join_digest=null; + if(tmp == null){ + log.error("received null digest from GET_DIGEST: will cause JOIN to fail"); + } + else { + // create a new digest, which contains the new member + join_digest=new MutableDigest(tmp.size() + new_mbrs.size()); + join_digest.add(tmp); // add the existing digest to the new one + for(Address member:new_mbrs) + join_digest.add(member, 0, 0); // ... and add the new members. their first seqno will be 1 + } + join_rsp=new JoinRsp(new_view, join_digest != null? join_digest.copy() : null); + } + + sendLeaveResponses(leaving_mbrs); // no-op if no leaving members + gms.castViewChangeWithDest(new_view, null,join_rsp,new_mbrs); + } + finally { + if(hasJoiningMembers) + gms.getDownProtocol().down(new Event(Event.RESUME_STABLE)); + if(!joinAndStateTransferInitiated) + gms.stopFlush(); + if(leaving) { + gms.initState(); // in case connect() is called again + } + } + } + + + /** + * Called by the GMS when a VIEW is received. + * @param new_view The view to be installed + * @param digest If view is a MergeView, digest contains the seqno digest of all members and has to + * be set by GMS + */ + public void handleViewChange(View new_view, Digest digest) { + Vector
        mbrs=new_view.getMembers(); + if(log.isDebugEnabled()) { + if(digest != null) + log.debug("view=" + new_view + ", digest=" + digest); + else + log.debug("view=" + new_view); + } + + if(leaving && !mbrs.contains(gms.local_addr)) + return; + gms.installView(new_view, digest); + } + + public void handleExit() { + cancelMerge(); + } + + public void stop() { + super.stop(); // sets leaving=false + stopMergeTask(); + } + + /* ------------------------------------------ Private methods ----------------------------------------- */ + + void startMergeTask(Vector
        coords) { + synchronized(merge_task) { + merge_task.start(coords); + } + } + + void stopMergeTask() { + synchronized(merge_task) { + merge_task.stop(); + } + } + + private void sendLeaveResponses(Collection
        leaving_members) { + for(Address address:leaving_members){ + Message msg=new Message(address, null, null); // send an ack to the leaving member + msg.setFlag(Message.OOB); + GMS.GmsHeader hdr=new GMS.GmsHeader(GMS.GmsHeader.LEAVE_RSP); + msg.putHeader(gms.getName(), hdr); + gms.getDownProtocol().down(new Event(Event.ENABLE_UNICASTS_TO,address)); + gms.getDownProtocol().down(new Event(Event.MSG, msg)); + } + } + + + /** + * Sends a MERGE_REQ to all coords and populates a list of MergeData (in merge_rsps). Returns after coords.size() + * response have been received, or timeout msecs have elapsed (whichever is first).

        + * If a subgroup coordinator rejects the MERGE_REQ (e.g. because of participation in a different merge), + * that member will be removed from coords ! + * @param coords A list of Addresses of subgroup coordinators (inluding myself) + * @param timeout Max number of msecs to wait for the merge responses from the subgroup coords + */ + private boolean getMergeDataFromSubgroupCoordinators(final Vector

        coords, long timeout) { + + boolean gotAllResponses = false; + long start=System.currentTimeMillis(); + synchronized(merge_rsps) { + merge_rsps.removeAllElements(); + if(log.isDebugEnabled()) + log.debug("Merge leader " + gms.local_addr + " sending MERGE_REQ to " + coords); + + for(Address coord:coords) { + // this allows UNICAST to remove coord from previous_members in case of a merge + gms.getDownProtocol().down(new Event(Event.ENABLE_UNICASTS_TO, coord)); + + Message msg=new Message(coord, null, null); + msg.setFlag(Message.OOB); + GMS.GmsHeader hdr=new GMS.GmsHeader(GMS.GmsHeader.MERGE_REQ); + hdr.mbr=gms.local_addr; + hdr.merge_id=merge_id; + msg.putHeader(gms.getName(), hdr); + gms.getDownProtocol().down(new Event(Event.MSG, msg)); + + if(log.isDebugEnabled()) + log.debug("Merge leader " + gms.local_addr + " sent MERGE_REQ to " + coord); + } + + // wait until num_rsps_expected >= num_rsps or timeout elapsed + int num_rsps_expected=coords.size(); + long curr_time=System.currentTimeMillis(); + long end_time=curr_time + timeout; + while(end_time > curr_time && !gotAllResponses) { + long time_to_wait=end_time - curr_time; + if(log.isDebugEnabled()) log.debug("Merge leader " + gms.local_addr +" waiting " + time_to_wait + " msecs for merge responses"); + if(merge_rsps.size() < num_rsps_expected) { + try { + merge_rsps.wait(500); + } + catch(Exception ex) { + } + } + if(log.isDebugEnabled()) + log.debug("Merge leader " + gms.local_addr +" expects " + num_rsps_expected + " responses, so far got " + merge_rsps.size() + " responses"); + + gotAllResponses = merge_rsps.size() >= num_rsps_expected; + curr_time=System.currentTimeMillis(); + } + long stop=System.currentTimeMillis(); + if(log.isDebugEnabled()) + log.debug("Merge leader " + gms.local_addr + " collected " + merge_rsps.size() + " merge response(s) in " + (stop-start) + " ms"); + } + return gotAllResponses; + } + + /** + * Generates a unique merge id by taking the local address and the current time + */ + private ViewId generateMergeId() { + return new ViewId(gms.local_addr, System.currentTimeMillis()); + // we're (ab)using ViewId as a merge id + } + + /** + * Merge all MergeData. All MergeData elements should be disjunct (both views and digests). However, + * this method is prepared to resolve duplicate entries (for the same member). Resolution strategy for + * views is to merge only 1 of the duplicate members. Resolution strategy for digests is to take the higher + * seqnos for duplicate digests.

        + * After merging all members into a Membership and subsequent sorting, the first member of the sorted membership + * will be the new coordinator. This method has a lock on merge_rsps. + * @param merge_rsps A list of MergeData items. Elements with merge_rejected=true were removed before. Is guaranteed + * not to be null and to contain at least 1 member. + */ + private MergeData consolidateMergeData(Vector merge_rsps) { + MergeData ret; + long logical_time=0; // for new_vid + ViewId new_vid, tmp_vid; + MergeView new_view; + View tmp_view; + Membership new_mbrs=new Membership(); + Address new_coord; + Vector subgroups=new Vector(11); + // contains a list of Views, each View is a subgroup + + for(MergeData tmp_data:merge_rsps) { + if(log.isDebugEnabled()) log.debug("Merge leader " + gms.local_addr + " is consolidating merge data " + tmp_data); + tmp_view=tmp_data.getView(); + if(tmp_view != null) { + tmp_vid=tmp_view.getVid(); + if(tmp_vid != null) { + // compute the new view id (max of all vids +1) + logical_time=Math.max(logical_time, tmp_vid.getId()); + } + // merge all membership lists into one (prevent duplicates) + new_mbrs.add(tmp_view.getMembers()); + subgroups.addElement((View)tmp_view.clone()); + } + } + + // the new coordinator is the first member of the consolidated & sorted membership list + new_mbrs.sort(); + new_coord = new_mbrs.size() > 0 ? new_mbrs.elementAt(0) : null; + if(new_coord == null) { + if(log.isErrorEnabled()) log.error("new_coord == null"); + return null; + } + // should be the highest view ID seen up to now plus 1 + new_vid=new ViewId(new_coord, logical_time + 1); + + // determine the new view + new_view=new MergeView(new_vid, new_mbrs.getMembers(), subgroups); + if(log.isDebugEnabled()) + log.debug("Merge leader " + gms.local_addr + " computed new merged view that will be " + new_view); + + // determine the new digest + Digest new_digest=consolidateDigests(merge_rsps, new_mbrs.size()); + if(new_digest == null) { + if(log.isErrorEnabled()) log.error("Merge leader " + gms.local_addr + "could not consolidate digest for merge"); + return null; + } + if(log.isDebugEnabled()) log.debug("Merge leader " + gms.local_addr + "consolidated digest=" + new_digest); + ret=new MergeData(gms.local_addr, new_view, new_digest); + return ret; + } + + /** + * Merge all digests into one. For each sender, the new value is min(low_seqno), max(high_seqno), + * max(high_seqno_seen). This method has a lock on merge_rsps + */ + private Digest consolidateDigests(Vector merge_rsps, int num_mbrs) { + MutableDigest retval=new MutableDigest(num_mbrs); + + for(MergeData data:merge_rsps) { + Digest tmp_digest=data.getDigest(); + if(tmp_digest == null) { + if(log.isErrorEnabled()) log.error("tmp_digest == null; skipping"); + continue; + } + retval.merge(tmp_digest); + } + return retval.copy(); + } + + /** + * Sends the new view and digest to all subgroup coordinors in coords. Each coord will in turn + *

          + *
        1. cast the new view and digest to all the members of its subgroup (MergeView) + *
        2. on reception of the view, if it is a MergeView, each member will set the digest and install + * the new view + *
        + */ + private void sendMergeView(Vector
        coords, MergeData combined_merge_data) { + View v; + Digest d; + + if(coords == null || combined_merge_data == null) + return; + + v=combined_merge_data.view; + d=combined_merge_data.digest; + if(v == null || d == null) { + if(log.isErrorEnabled()) log.error("view or digest is null, cannot send consolidated merge view/digest"); + return; + } + + if(log.isDebugEnabled()) + log.debug(gms.local_addr + " is sending merge view " + v.getVid() + " to coordinators " + coords); + + gms.merge_ack_collector.reset(v.getVid(), coords); + int size=gms.merge_ack_collector.size(); + long timeout=gms.view_ack_collection_timeout; + + long start = System.currentTimeMillis(); + for(Address coord:coords) { + Message msg=new Message(coord, null, null); + GMS.GmsHeader hdr=new GMS.GmsHeader(GMS.GmsHeader.INSTALL_MERGE_VIEW); + hdr.view=v; + hdr.my_digest=d; + hdr.merge_id=merge_id; + msg.putHeader(gms.getName(), hdr); + gms.getDownProtocol().down(new Event(Event.MSG, msg)); + } + + //[JGRP-700] - FLUSH: flushing should span merge + //if flush is in stack wait for acks from separated island coordinators + if(gms.flushProtocolInStack) { + try { + gms.merge_ack_collector.waitForAllAcks(timeout); + long stop=System.currentTimeMillis(); + if(log.isTraceEnabled()) + log.trace("received all ACKs (" + size + + ") for merged view " + + v + + " in " + + (stop - start) + + "ms"); + } + catch(TimeoutException e) { + log.warn("Merge coordinator " + gms.local_addr + " failed to collect all ACKs for merge (" + size + + ") for view " + + v + + " after " + + timeout + + "ms, missing ACKs from " + + gms.merge_ack_collector.printMissing() + + " (received=" + + gms.merge_ack_collector.printReceived() + + "), local_addr=" + + gms.local_addr); + } + } + } + + /** + * Send back a response containing view and digest to sender + */ + private void sendMergeResponse(Address sender, View view, Digest digest) { + Message msg=new Message(sender, null, null); + msg.setFlag(Message.OOB); + GMS.GmsHeader hdr=new GMS.GmsHeader(GMS.GmsHeader.MERGE_RSP); + hdr.merge_id=merge_id; + hdr.view=view; + hdr.my_digest=digest; + msg.putHeader(gms.getName(), hdr); + if(log.isDebugEnabled()) log.debug("response=" + hdr); + gms.getDownProtocol().down(new Event(Event.ENABLE_UNICASTS_TO, sender)); + gms.getDownProtocol().down(new Event(Event.MSG, msg)); + } + + + private void sendMergeCancelledMessage(Vector
        coords, ViewId merge_id) { + if(coords == null || merge_id == null) { + if(log.isErrorEnabled()) log.error("coords or merge_id == null"); + return; + } + for(Address coord:coords) { + Message msg=new Message(coord, null, null); + // msg.setFlag(Message.OOB); + GMS.GmsHeader hdr=new GMS.GmsHeader(GMS.GmsHeader.CANCEL_MERGE); + hdr.merge_id=merge_id; + msg.putHeader(gms.getName(), hdr); + gms.getDownProtocol().down(new Event(Event.MSG, msg)); + if(log.isDebugEnabled()) log.debug("Merge leader " + gms.local_addr + " send cancel merge to " + coord); + } + } + + /** Removed rejected merge requests from merge_rsps and coords. This method has a lock on merge_rsps */ + private void removeRejectedMergeRequests(Vector
        coords) { + for(Iterator it=merge_rsps.iterator(); it.hasNext();) { + MergeData data=it.next(); + if(data.merge_rejected) { + if(data.getSender() != null && coords != null) + coords.removeElement(data.getSender()); + it.remove(); + if(log.isDebugEnabled()) log.debug("removed element " + data); + } + } + } + + /* --------------------------------------- End of Private methods ------------------------------------- */ + + /** + * Starts the merge protocol (only run by the merge leader). Essentially sends a MERGE_REQ to all + * coordinators of all subgroups found. Each coord receives its digest and view and returns it. + * The leader then computes the digest and view for the new group from the return values. Finally, it + * sends this merged view/digest to all subgroup coordinators; each coordinator will install it in their + * subgroup. + */ + private class MergeTask implements Runnable { + Thread t=null; + Vector
        coords=null; // list of subgroup coordinators to be contacted + + public void start(Vector
        groupCoord) { + this.coords = groupCoord != null ? new Vector
        (groupCoord) : null; + if(!isRunning()) { + t=gms.getThreadFactory().newThread(this, "MergeTask"); + t.setDaemon(true); + t.start(); + } + } + + public void stop() { + Thread tmp=t; + if(isRunning()) { + t=null; + tmp.interrupt(); + } + t=null; + } + + public boolean isRunning() { + return t != null && t.isAlive(); + } + + /** + * Runs the merge protocol as a leader + */ + public void run() { + if(merging) { + if(log.isWarnEnabled()) + log.warn(gms.local_addr + " running merge task, but merge is is already in progress, terminating"); + return; + } + + if(coords == null || coords.size() <= 1) { + if(log.isErrorEnabled()) + log.error("coords == null or size <= 1"); + return; + } + + if(log.isDebugEnabled()) + log.debug(gms.local_addr + " running merge task, coordinators are " + this.coords); + Vector
        coordsCopy=new Vector
        (coords); + /* 1. Generate a merge_id that uniquely identifies the merge in progress */ + ViewId generatedMergeId=generateMergeId(); + + try { + setMergeId(generatedMergeId); + + /* 2. Fetch the current Views/Digests from all subgroup coordinators */ + boolean success=getMergeDataFromSubgroupCoordinators(coords, gms.merge_timeout); + + if(!success) { + throw new Exception("Merge aborted. Merge leader did not get MergeData from all subgroup coordinators " + coords); + } + + /* + * 3. Remove rejected MergeData elements from merge_rsp and + * coords (so we'll send the new view only to members who + * accepted the merge request) + */ + MergeData combined_merge_data=null; + synchronized(merge_rsps) { + removeRejectedMergeRequests(coords); + if(merge_rsps.size() <= 1) { + throw new Exception("Merge leader " + gms.local_addr + + " did not get all merge responses from subgroup coordinators (" + + merge_rsps + ")"); + } + else { + /* 4. Combine all views and digests into 1 View/1 Digest */ + combined_merge_data=consolidateMergeData(merge_rsps); + if(combined_merge_data == null) { + throw new Exception("Merge leader " + gms.local_addr + + " could not consolidate merge"); + } + } + } + /* 4. Send the new View/Digest to all coordinators (including myself). On reception, they will + install the digest and view in all of their subgroup members */ + sendMergeView(coords, combined_merge_data); + } + catch(Throwable ex) { + if(log.isWarnEnabled()) + log.warn(ex.getLocalizedMessage()); + sendMergeCancelledMessage(coordsCopy, generatedMergeId); + } + finally { + gms.getViewHandler().resume(generatedMergeId); + stopMergeCanceller(); // this is probably not necessary + + /*5. if flush is in stack stop the flush for entire cluster + [JGRP-700] - FLUSH: flushing should span merge */ + + gms.stopFlush(); + + merging=false; + if(log.isDebugEnabled()) + log.debug("Merge leader " + gms.local_addr + " completed merge task"); + t=null; + } + } + } + + + private class MergeCanceller implements Runnable { + private Object my_merge_id=null; + private long timeout; + + MergeCanceller(Object my_merge_id, long timeout) { + this.my_merge_id=my_merge_id; + this.timeout=timeout; + } + + + public void run() { + if(merge_id != null && my_merge_id.equals(merge_id)) { + if(log.isDebugEnabled()) + log.debug("At " + gms.local_addr + " cancelling merge due to timer timeout (" + timeout + " ms)"); + cancelMerge(); + } + else { + if(log.isWarnEnabled()) + log.warn("At " + gms.local_addr +" timer kicked in after " + timeout + " ms, but no (or different) merge was in progress: " + + "merge_id=" + merge_id + ", my_merge_id=" + my_merge_id); + } + } + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/FLUSH.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/FLUSH.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/FLUSH.java 17 Aug 2012 14:51:23 -0000 1.1 @@ -0,0 +1,1018 @@ +package org.jgroups.protocols.pbcast; + +import org.jgroups.*; +import org.jgroups.annotations.GuardedBy; +import org.jgroups.stack.Protocol; +import org.jgroups.util.Digest; +import org.jgroups.util.Promise; +import org.jgroups.util.Streamable; +import org.jgroups.util.Util; + +import java.io.*; +import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Flush, as it name implies, forces group members to flush their pending + * messages while blocking them to send any additional messages. The process of + * flushing acquiesces the group so that state transfer or a join can be done. + * It is also called stop-the-world model as nobody will be able to send + * messages while a flush is in process. + * + *

        + * Flush is needed for: + *

        + * (1) State transfer. When a member requests state transfer, the coordinator + * tells everyone to stop sending messages and waits for everyone's ack. Then it + * asks the application for its state and ships it back to the requester. After + * the requester has received and set the state successfully, the coordinator + * tells everyone to resume sending messages. + *

        + * (2) View changes (e.g.a join). Before installing a new view V2, flushing + * would ensure that all messages *sent* in the current view V1 are indeed + * *delivered* in V1, rather than in V2 (in all non-faulty members). This is + * essentially Virtual Synchrony. + * + * + * + * @author Vladimir Blagojevic + * @version $Id: FLUSH.java,v 1.1 2012/08/17 14:51:23 marcin Exp $ + * @since 2.4 + */ +public class FLUSH extends Protocol { + + public static final String NAME = "FLUSH"; + + + /* ------------------------------------------ Properties ------------------------------------------ */ + private long timeout = 8000; + + private long start_flush_timeout = 2000; + + private boolean enable_reconciliation = true; + + + /* --------------------------------------------- JMX ---------------------------------------------- */ + + + private long startFlushTime; + + private long totalTimeInFlush; + + private int numberOfFlushes; + + private double averageFlushDuration; + + + + /* --------------------------------------------- Fields ------------------------------------------------------ */ + + + @GuardedBy("sharedLock") + private View currentView; + + private Address localAddress; + + /** + * Group member that requested FLUSH. For view installations flush + * coordinator is the group coordinator For state transfer flush coordinator + * is the state requesting member + */ + @GuardedBy("sharedLock") + private Address flushCoordinator; + + @GuardedBy("sharedLock") + private final List

        flushMembers; + + private final AtomicInteger viewCounter = new AtomicInteger(0); + + @GuardedBy("sharedLock") + private final Map flushCompletedMap; + + @GuardedBy("sharedLock") + private final List
        flushNotCompletedMap; + + + @GuardedBy("sharedLock") + private final Set
        suspected; + + @GuardedBy("sharedLock") + private final List
        reconcileOks; + + private final Object sharedLock = new Object(); + + private final Object blockMutex = new Object(); + + /** + * Indicates if FLUSH.down() is currently blocking threads Condition + * predicate associated with blockMutex + */ + @GuardedBy("blockMutex") + private volatile boolean isBlockingFlushDown = true; + + @GuardedBy("sharedLock") + private boolean flushCompleted = false; + + private volatile boolean allowMessagesToPassUp = false; + + private final Promise flush_promise = new Promise(); + + private final AtomicBoolean flushInProgress = new AtomicBoolean(false); + + private final AtomicBoolean sentBlock = new AtomicBoolean(false); + + private final AtomicBoolean sentUnblock = new AtomicBoolean(false); + + + + public FLUSH(){ + super(); + currentView = new View(new ViewId(), new Vector
        ()); + flushCompletedMap = new HashMap(); + flushNotCompletedMap = new ArrayList
        (); + reconcileOks = new ArrayList
        (); + flushMembers = new ArrayList
        (); + suspected = new TreeSet
        (); + } + + public String getName() { + return NAME; + } + + public long getStartFlushTimeout() { + return start_flush_timeout; + } + + public void setStartFlushTimeout(long start_flush_timeout) { + this.start_flush_timeout=start_flush_timeout; + } + + public boolean setProperties(Properties props) { + super.setProperties(props); + + timeout = Util.parseLong(props, "timeout", timeout); + start_flush_timeout = Util.parseLong(props, "start_flush_timeout", start_flush_timeout); + enable_reconciliation = Util.parseBoolean(props, + "enable_reconciliation", + enable_reconciliation); + + + String str = props.getProperty("retry_timeout"); + if(str != null){ + log.warn("retry_timeout has been deprecated and its value will be ignored"); + props.remove("retry_timeout"); + } + + str = props.getProperty("flush_retry_count"); + if(str != null){ + log.warn("flush_retry_count has been deprecated and its value will be ignored"); + props.remove("flush_retry_count"); + } + + str = props.getProperty("auto_flush_conf"); + if(str != null){ + log.warn("auto_flush_conf has been deprecated and its value will be ignored"); + props.remove("auto_flush_conf"); + } + + if(!props.isEmpty()){ + log.error("the following properties are not recognized: " + props); + return false; + } + return true; + } + + public void start() throws Exception { + Map map = new HashMap(); + map.put("flush_supported", Boolean.TRUE); + up_prot.up(new Event(Event.CONFIG, map)); + down_prot.down(new Event(Event.CONFIG, map)); + + viewCounter.set(0); + synchronized(blockMutex){ + isBlockingFlushDown = true; + } + allowMessagesToPassUp = false; + } + + public void stop() { + synchronized(sharedLock){ + currentView = new View(new ViewId(), new Vector
        ()); + flushCompletedMap.clear(); + flushNotCompletedMap.clear(); + flushMembers.clear(); + suspected.clear(); + flushCoordinator = null; + } + } + + /* -------------------JMX attributes and operations --------------------- */ + public double getAverageFlushDuration() { + return averageFlushDuration; + } + + public long getTotalTimeInFlush() { + return totalTimeInFlush; + } + + public int getNumberOfFlushes() { + return numberOfFlushes; + } + + public boolean startFlush() { + return startFlush(new Event(Event.SUSPEND)); + } + + private boolean startFlush(Event evt){ + if(log.isDebugEnabled()) + log.debug("Received " + evt + " at " + localAddress + ". Running FLUSH..."); + + List
        flushParticipants = (List
        ) evt.getArg(); + return startFlush(flushParticipants); + } + + private boolean startFlush(List
        flushParticipants) { + boolean successfulFlush = false; + if (!flushInProgress.get()) { + flush_promise.reset(); + onSuspend(flushParticipants); + try { + Boolean r = flush_promise.getResultWithTimeout(start_flush_timeout); + successfulFlush = r.booleanValue(); + } catch (TimeoutException e) { + if (log.isDebugEnabled()) + log.debug("At " + localAddress + + " timed out waiting for flush responses after " + + start_flush_timeout + " msec"); + } + } + return successfulFlush; + } + + public void stopFlush() { + down(new Event(Event.RESUME)); + } + + /* + * ------------------- end JMX attributes and operations + * --------------------- + */ + + public Object down(Event evt) { + switch(evt.getType()){ + case Event.MSG: + Message msg = (Message) evt.getArg(); + Address dest = msg.getDest(); + if(dest == null || dest.isMulticastAddress()){ + //mcasts + FlushHeader fh = (FlushHeader) msg.getHeader(getName()); + if(fh != null && fh.type == FlushHeader.FLUSH_BYPASS){ + return down_prot.down(evt); + } + else{ + blockMessageDuringFlush(); + } + }else{ + //unicasts are irrelevant in virtual synchrony, let them through + return down_prot.down(evt); + } + break; + + case Event.CONNECT: + case Event.CONNECT_WITH_STATE_TRANSFER: + if(sentBlock.compareAndSet(false, true)){ + sendBlockUpToChannel(); + } + + Object result=down_prot.down(evt); + if(result instanceof Throwable) { + sentBlock.set(false); // set the var back to its original state if we cannot connect successfully + } + return result; + + case Event.SUSPEND: + return startFlush(evt); + + case Event.RESUME: + onResume(evt); + return null; + } + return down_prot.down(evt); + } + + private void blockMessageDuringFlush() { + boolean shouldSuspendByItself = false; + long start = 0, stop = 0; + synchronized(blockMutex){ + while(isBlockingFlushDown){ + if(log.isDebugEnabled()) + log.debug("FLUSH block at " + localAddress + + " for " + + (timeout <= 0 ? "ever" : timeout + "ms")); + try{ + start = System.currentTimeMillis(); + if(timeout <= 0) + blockMutex.wait(); + else + blockMutex.wait(timeout); + stop = System.currentTimeMillis(); + }catch(InterruptedException e){ + Thread.currentThread().interrupt(); // set interrupt flag again + } + if(isBlockingFlushDown){ + isBlockingFlushDown = false; + shouldSuspendByItself = true; + blockMutex.notifyAll(); + } + } + } + if(shouldSuspendByItself){ + log.warn("unblocking FLUSH.down() at " + localAddress + + " after timeout of " + + (stop - start) + + "ms"); + flush_promise.setResult(Boolean.TRUE); + } + } + + public Object up(Event evt) { + + switch(evt.getType()){ + case Event.MSG: + Message msg = (Message) evt.getArg(); + final FlushHeader fh = (FlushHeader) msg.getHeader(getName()); + if(fh != null){ + switch(fh.type){ + case FlushHeader.FLUSH_BYPASS: + return up_prot.up(evt); + case FlushHeader.START_FLUSH: + Collection
        fp=fh.flushParticipants; + boolean amIParticipant = (fp != null && fp.contains(localAddress)) || msg.getSrc().equals(localAddress); + if(amIParticipant){ + handleStartFlush(msg, fh); + } + else{ + if (log.isDebugEnabled()) + log.debug("Received START_FLUSH at " + localAddress + + " but I am not flush participant, not responding"); + } + break; + case FlushHeader.FLUSH_RECONCILE: + handleFlushReconcile(msg, fh); + break; + case FlushHeader.FLUSH_RECONCILE_OK: + onFlushReconcileOK(msg); + break; + case FlushHeader.STOP_FLUSH: + onStopFlush(); + break; + case FlushHeader.ABORT_FLUSH: + Collection
        flushParticipants = fh.flushParticipants; + + if(flushParticipants != null && flushParticipants.contains(localAddress)){ + if (log.isDebugEnabled()) { + log.debug("At " + localAddress + + " received ABORT_FLUSH from flush coordinator " + msg.getSrc() + + ", am i flush participant=" + + flushParticipants.contains(localAddress)); + } + flushInProgress.set(false); + flushNotCompletedMap.clear(); + flushCompletedMap.clear(); + } + break; + case FlushHeader.FLUSH_NOT_COMPLETED: + if (log.isDebugEnabled()) { + log.debug("At " + localAddress + + " received FLUSH_NOT_COMPLETED from " + + msg.getSrc()); + } + boolean flushCollision = false; + synchronized(sharedLock){ + flushNotCompletedMap.add(msg.getSrc()); + flushCollision = !flushCompletedMap.isEmpty(); + if(flushCollision){ + flushNotCompletedMap.clear(); + flushCompletedMap.clear(); + } + } + + if (log.isDebugEnabled()) { + log.debug("At " + localAddress + + " received FLUSH_NOT_COMPLETED from " + + msg.getSrc() + " collision=" + flushCollision); + } + + //reject flush if we have at least one OK and at least one FAIL + if(flushCollision){ + Runnable r = new Runnable(){ + public void run() { + //wait a bit so ABORTs do not get received before other possible FLUSH_COMPLETED + Util.sleep(1000); + rejectFlush(fh.flushParticipants, fh.viewID); + } + }; + new Thread(r).start(); + } + //however, flush should fail/retry as soon as one FAIL is received + flush_promise.setResult(Boolean.FALSE); + break; + + case FlushHeader.FLUSH_COMPLETED: + if(isCurrentFlushMessage(fh)) + onFlushCompleted(msg.getSrc(), fh); + break; + } + return null; // do not pass FLUSH msg up + }else{ + // http://jira.jboss.com/jira/browse/JGRP-575 + // for processing of application messages after we join, + // lets wait for STOP_FLUSH to complete + // before we start allowing message up. + Address dest=msg.getDest(); + if(dest != null && !dest.isMulticastAddress()) { + return up_prot.up(evt); // allow unicasts to pass, virtual synchrony olny applies to multicasts + } + + if(!allowMessagesToPassUp) + return null; + } + break; + + case Event.VIEW_CHANGE: + /* + * [JGRP-618] - FLUSH coordinator transfer reorders + * block/unblock/view events in applications (TCP stack only) + * + */ + up_prot.up(evt); + View newView = (View) evt.getArg(); + boolean coordinatorLeft = onViewChange(newView); + boolean singletonMember = newView.size() == 1 && newView.containsMember(localAddress); + boolean isThisOurFirstView = viewCounter.addAndGet(1) == 1; + // if this is channel's first view and its the only member of the group - no flush was run + // but the channel application should still receive BLOCK,VIEW,UNBLOCK + + //also if coordinator of flush left each member should run stopFlush individually. + if((isThisOurFirstView && singletonMember) || coordinatorLeft){ + onStopFlush(); + } + return null; + + case Event.TMP_VIEW: + /* + * April 25, 2007 + * + * Accommodating current NAKACK (1.127) + * + * Updates field currentView of a leaving coordinator. Leaving + * coordinator, after it sends out the view, does not need to + * participate in second flush phase. + * + * see onStopFlush(); + * + * TODO: revisit if still needed post NAKACK 1.127 + * + */ + View tmpView = (View) evt.getArg(); + if(!tmpView.containsMember(localAddress)){ + onViewChange(tmpView); + } + break; + + case Event.SET_LOCAL_ADDRESS: + localAddress = (Address) evt.getArg(); + break; + + case Event.SUSPECT: + onSuspect((Address) evt.getArg()); + break; + + case Event.SUSPEND: + return startFlush(evt); + + case Event.RESUME: + onResume(evt); + return null; + + } + + return up_prot.up(evt); + } + + private void onFlushReconcileOK(Message msg) { + if(log.isDebugEnabled()) + log.debug(localAddress + " received reconcile ok from " + msg.getSrc()); + + synchronized(sharedLock){ + reconcileOks.add(msg.getSrc()); + if(reconcileOks.size() >= flushMembers.size()){ + flush_promise.setResult(Boolean.TRUE); + if(log.isDebugEnabled()) + log.debug("All FLUSH_RECONCILE_OK received at " + localAddress); + } + } + } + + private void handleFlushReconcile(Message msg, FlushHeader fh) { + Address requester = msg.getSrc(); + Digest reconcileDigest = fh.digest; + + if(log.isDebugEnabled()) + log.debug("Received FLUSH_RECONCILE at " + localAddress + + " passing digest to NAKACK " + + reconcileDigest); + + // Let NAKACK reconcile missing messages + down_prot.down(new Event(Event.REBROADCAST, reconcileDigest)); + + if(log.isDebugEnabled()) + log.debug("Returned from FLUSH_RECONCILE at " + localAddress + + " Sending RECONCILE_OK to " + + requester + + ", thread " + + Thread.currentThread()); + + Message reconcileOk = new Message(requester); + reconcileOk.setFlag(Message.OOB); + reconcileOk.putHeader(getName(), new FlushHeader(FlushHeader.FLUSH_RECONCILE_OK)); + down_prot.down(new Event(Event.MSG, reconcileOk)); + } + + private void handleStartFlush(Message msg, FlushHeader fh) { + Address flushRequester = msg.getSrc(); + + boolean proceed = flushInProgress.compareAndSet(false, true); + if (proceed) { + synchronized (sharedLock) { + flushCoordinator = flushRequester; + } + onStartFlush(flushRequester, fh); + } + else{ + FlushHeader fhr=new FlushHeader(FlushHeader.FLUSH_NOT_COMPLETED, fh.viewID,fh.flushParticipants); + Message response=new Message(flushRequester); + response.putHeader(getName(), fhr); + down_prot.down(new Event(Event.MSG, response)); + if(log.isDebugEnabled()) + log.debug("Received START_FLUSH at " + localAddress + + " responded with FLUSH_NOT_COMPLETED to " + + flushRequester); + } + } + + private void rejectFlush(Collection
        participants,long viewId) { + for(Address flushMember:participants){ + Message reject = new Message(flushMember, localAddress, null); + reject.putHeader(getName(), new FlushHeader(FlushHeader.ABORT_FLUSH,viewId,participants)); + down_prot.down(new Event(Event.MSG, reject)); + } + } + + public Vector providedDownServices() { + Vector retval = new Vector(2); + retval.addElement(new Integer(Event.SUSPEND)); + retval.addElement(new Integer(Event.RESUME)); + return retval; + } + + private void sendBlockUpToChannel() { + up_prot.up(new Event(Event.BLOCK)); + sentUnblock.set(false); + } + + private void sendUnBlockUpToChannel() { + sentBlock.set(false); + up_prot.up(new Event(Event.UNBLOCK)); + } + + private boolean isCurrentFlushMessage(FlushHeader fh) { + return fh.viewID == currentViewId(); + } + + private long currentViewId() { + long viewId = -1; + synchronized(sharedLock){ + ViewId view = currentView.getVid(); + if(view != null){ + viewId = view.getId(); + } + } + return viewId; + } + + private boolean onViewChange(View view) { + boolean coordinatorLeft = false; + synchronized(sharedLock){ + suspected.retainAll(view.getMembers()); + currentView = view; + coordinatorLeft =!view.getMembers().isEmpty() && !view.containsMember(view.getCreator()); + } + if(log.isDebugEnabled()) + log.debug("Installing view at " + localAddress + " view is " + view); + + return coordinatorLeft; + } + + private void onStopFlush() { + if(stats){ + long stopFlushTime = System.currentTimeMillis(); + totalTimeInFlush += (stopFlushTime - startFlushTime); + if(numberOfFlushes > 0){ + averageFlushDuration = totalTimeInFlush / (double) numberOfFlushes; + } + } + + synchronized(sharedLock){ + flushCompletedMap.clear(); + flushNotCompletedMap.clear(); + flushMembers.clear(); + suspected.clear(); + flushCoordinator = null; + allowMessagesToPassUp = true; + flushCompleted = false; + } + + if(log.isDebugEnabled()) + log.debug("At " + localAddress + + " received STOP_FLUSH, unblocking FLUSH.down() and sending UNBLOCK up"); + + synchronized(blockMutex){ + isBlockingFlushDown = false; + blockMutex.notifyAll(); + } + + if(sentUnblock.compareAndSet(false,true)){ + //ensures that we do not repeat unblock event + sendUnBlockUpToChannel(); + } + flushInProgress.set(false); + } + + private void onSuspend(List
        members) { + Message msg = null; + Collection
        participantsInFlush = null; + synchronized(sharedLock){ + // start FLUSH only on group members that we need to flush + if(members != null){ + participantsInFlush = members; + participantsInFlush.retainAll(currentView.getMembers()); + }else{ + participantsInFlush = new ArrayList
        (currentView.getMembers()); + } + msg = new Message(null, localAddress, null); + msg.putHeader(getName(), new FlushHeader(FlushHeader.START_FLUSH, + currentViewId(), + participantsInFlush)); + } + if(participantsInFlush.isEmpty()){ + flush_promise.setResult(Boolean.TRUE); + }else{ + down_prot.down(new Event(Event.MSG, msg)); + if(log.isDebugEnabled()) + log.debug("Flush coordinator " + localAddress + + " is starting FLUSH with participants " + + participantsInFlush); + } + } + + private void onResume(Event evt) { + List
        members = (List
        ) evt.getArg(); + long viewID = currentViewId(); + if(members == null || members.isEmpty()){ + Message msg = new Message(null, localAddress, null); + //Cannot be OOB since START_FLUSH is not OOB + //we have to FIFO order two subsequent flushes + msg.putHeader(getName(), new FlushHeader(FlushHeader.STOP_FLUSH, viewID)); + down_prot.down(new Event(Event.MSG, msg)); + if(log.isDebugEnabled()) + log.debug("Received RESUME at " + localAddress + ", sent STOP_FLUSH to all"); + }else{ + for (Address address : members) { + Message msg = new Message(address, localAddress, null); + //Cannot be OOB since START_FLUSH is not OOB + //we have to FIFO order two subsequent flushes + msg.putHeader(getName(), new FlushHeader(FlushHeader.STOP_FLUSH, viewID)); + down_prot.down(new Event(Event.MSG, msg)); + if(log.isDebugEnabled()) + log.debug("Received RESUME at " + localAddress + ", sent STOP_FLUSH to " + address); + } + } + } + + private void onStartFlush(Address flushStarter, FlushHeader fh) { + if(stats){ + startFlushTime = System.currentTimeMillis(); + numberOfFlushes += 1; + } + boolean proceed = false; + synchronized(sharedLock){ + flushCoordinator = flushStarter; + flushMembers.clear(); + if(fh.flushParticipants != null){ + flushMembers.addAll(fh.flushParticipants); + } + proceed = flushMembers.contains(localAddress); + flushMembers.removeAll(suspected); + } + + if(proceed) { + if(sentBlock.compareAndSet(false, true)) { + //ensures that we do not repeat block event + //and that we do not send block event to non participants + sendBlockUpToChannel(); + synchronized(blockMutex) { + isBlockingFlushDown=true; + } + } + else { + if(log.isDebugEnabled()) + log.debug("Received START_FLUSH at " + localAddress + + " but not sending BLOCK up"); + } + + Digest digest=(Digest)down_prot.down(new Event(Event.GET_DIGEST)); + FlushHeader fhr=new FlushHeader(FlushHeader.FLUSH_COMPLETED, fh.viewID,fh.flushParticipants); + fhr.addDigest(digest); + + Message msg=new Message(flushStarter); + msg.putHeader(getName(), fhr); + down_prot.down(new Event(Event.MSG, msg)); + if(log.isDebugEnabled()) + log.debug("Received START_FLUSH at " + localAddress + + " responded with FLUSH_COMPLETED to " + + flushStarter); + } + + } + + private void onFlushCompleted(Address address, final FlushHeader header) { + Message msg = null; + boolean needsReconciliationPhase = false; + boolean collision = false; + Digest digest = header.digest; + synchronized(sharedLock){ + flushCompletedMap.put(address, digest); + flushCompleted = flushCompletedMap.size() >= flushMembers.size() + && !flushMembers.isEmpty() + && flushCompletedMap.keySet().containsAll(flushMembers); + + collision = !flushNotCompletedMap.isEmpty(); + if(log.isDebugEnabled()) + log.debug("At " + localAddress + + " FLUSH_COMPLETED from " + + address + + ",completed " + + flushCompleted + + ",flushMembers " + + flushMembers + + ",flushCompleted " + + flushCompletedMap.keySet()); + + needsReconciliationPhase = enable_reconciliation && flushCompleted + && hasVirtualSynchronyGaps(); + if(needsReconciliationPhase){ + + Digest d = findHighestSequences(); + msg = new Message(); + msg.setFlag(Message.OOB); + FlushHeader fh = new FlushHeader(FlushHeader.FLUSH_RECONCILE, + currentViewId(), + flushMembers); + reconcileOks.clear(); + fh.addDigest(d); + msg.putHeader(getName(), fh); + + if(log.isDebugEnabled()) + log.debug("At "+ localAddress + " reconciling flush mebers due to virtual synchrony gap, digest is " + d + + " flush members are " + + flushMembers); + + flushCompletedMap.clear(); + } else if (flushCompleted){ + flushCompletedMap.clear(); + } else if (collision){ + flushNotCompletedMap.clear(); + flushCompletedMap.clear(); + } + } + if(needsReconciliationPhase){ + down_prot.down(new Event(Event.MSG, msg)); + }else if(flushCompleted){ + flush_promise.setResult(Boolean.TRUE); + if(log.isDebugEnabled()) + log.debug("All FLUSH_COMPLETED received at " + localAddress); + }else if(collision){ + //reject flush if we have at least one OK and at least one FAIL + Runnable r = new Runnable(){ + public void run() { + //wait a bit so ABORTs do not get received before other possible FLUSH_COMPLETED + Util.sleep(1000); + rejectFlush(header.flushParticipants, header.viewID); + } + }; + new Thread(r).start(); + } + } + + private boolean hasVirtualSynchronyGaps() { + ArrayList digests = new ArrayList(); + digests.addAll(flushCompletedMap.values()); + Digest firstDigest = digests.get(0); + List remainingDigests = digests.subList(1, digests.size()); + for(Digest digest:remainingDigests){ + Digest diff = firstDigest.difference(digest); + if(diff != Digest.EMPTY_DIGEST){ + return true; + } + } + return false; + } + + private Digest findHighestSequences() { + Digest result = null; + List digests = new ArrayList(flushCompletedMap.values()); + + result = digests.get(0); + List remainingDigests = digests.subList(1, digests.size()); + + for(Digest digestG:remainingDigests){ + result = result.highestSequence(digestG); + } + return result; + } + + private void onSuspect(Address address) { + + //handles FlushTest#testFlushWithCrashedFlushCoordinator + boolean amINeighbourOfCrashedFlushCoordinator = false; + ArrayList
        flushMembersCopy = null; + synchronized(sharedLock){ + boolean flushCoordinatorSuspected = address.equals(flushCoordinator); + if(flushCoordinatorSuspected && flushMembers != null){ + int indexOfCoordinator = flushMembers.indexOf(flushCoordinator); + int myIndex = flushMembers.indexOf(localAddress); + int diff = myIndex - indexOfCoordinator; + amINeighbourOfCrashedFlushCoordinator = (diff == 1 || (myIndex==0 && indexOfCoordinator == flushMembers.size())); + if(amINeighbourOfCrashedFlushCoordinator){ + flushMembersCopy = new ArrayList
        (flushMembers); + } + } + } + if(amINeighbourOfCrashedFlushCoordinator){ + if(log.isDebugEnabled()) + log.debug("Flush coordinator " + flushCoordinator + " suspected, " + localAddress + " is neighbour, completing flush "); + + onResume(new Event(Event.RESUME, flushMembersCopy)); + } + + //handles FlushTest#testFlushWithCrashedNonCoordinators + boolean flushOkCompleted = false; + Message m = null; + long viewID = 0; + synchronized(sharedLock){ + suspected.add(address); + flushMembers.removeAll(suspected); + viewID = currentViewId(); + flushOkCompleted = !flushCompletedMap.isEmpty() && flushCompletedMap.keySet().containsAll(flushMembers); + if(flushOkCompleted){ + m = new Message(flushCoordinator, localAddress, null); + } + if(log.isDebugEnabled()) + log.debug("Suspect is " + address + + ",completed " + + flushOkCompleted + + ", flushOkSet " + + flushCompletedMap + + " flushMembers " + + flushMembers); + } + if(flushOkCompleted){ + Digest digest = (Digest) down_prot.down(new Event(Event.GET_DIGEST)); + FlushHeader fh = new FlushHeader(FlushHeader.FLUSH_COMPLETED, viewID); + fh.addDigest(digest); + m.putHeader(getName(), fh); + down_prot.down(new Event(Event.MSG, m)); + if(log.isDebugEnabled()) + log.debug(localAddress + " sent FLUSH_COMPLETED message to " + flushCoordinator); + } + } + + public static class FlushHeader extends Header implements Streamable { + public static final byte START_FLUSH = 0; + + public static final byte STOP_FLUSH = 2; + + public static final byte FLUSH_COMPLETED = 3; + + public static final byte ABORT_FLUSH = 5; + + public static final byte FLUSH_BYPASS = 6; + + public static final byte FLUSH_RECONCILE = 7; + + public static final byte FLUSH_RECONCILE_OK = 8; + + public static final byte FLUSH_NOT_COMPLETED = 9; + + byte type; + + long viewID; + + Collection
        flushParticipants; + + Digest digest = null; + private static final long serialVersionUID=-6248843990215637687L; + + public FlushHeader(){ + this(START_FLUSH, 0); + } // used for externalization + + public FlushHeader(byte type){ + this(type, 0); + } + + public FlushHeader(byte type,long viewID){ + this(type, viewID, null); + } + + public FlushHeader(byte type,long viewID,Collection
        flushView){ + this.type = type; + this.viewID = viewID; + if(flushView != null){ + this.flushParticipants = new ArrayList
        (flushView); + } + } + + @Override + public int size() { + int retval=Global.BYTE_SIZE; // type + retval+=Global.LONG_SIZE; // viewID + retval+= Util.size(flushParticipants); + retval+=Global.BYTE_SIZE; // presence for digest + if(digest != null){ + retval += digest.serializedSize(); + } + return retval; + } + + public void addDigest(Digest digest) { + this.digest = digest; + } + + public String toString() { + switch(type){ + case START_FLUSH: + return "FLUSH[type=START_FLUSH,viewId=" + viewID + + ",members=" + + flushParticipants + + "]"; + case STOP_FLUSH: + return "FLUSH[type=STOP_FLUSH,viewId=" + viewID + "]"; + case ABORT_FLUSH: + return "FLUSH[type=ABORT_FLUSH,viewId=" + viewID + "]"; + case FLUSH_COMPLETED: + return "FLUSH[type=FLUSH_COMPLETED,viewId=" + viewID + "]"; + case FLUSH_BYPASS: + return "FLUSH[type=FLUSH_BYPASS,viewId=" + viewID + "]"; + case FLUSH_RECONCILE: + return "FLUSH[type=FLUSH_RECONCILE,viewId=" + viewID + ",digest=" + digest + "]"; + case FLUSH_RECONCILE_OK: + return "FLUSH[type=FLUSH_RECONCILE_OK,viewId=" + viewID + "]"; + default: + return "[FLUSH: unknown type (" + type + ")]"; + } + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeByte(type); + out.writeLong(viewID); + out.writeObject(flushParticipants); + out.writeObject(digest); + } + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + type = in.readByte(); + viewID = in.readLong(); + flushParticipants = (Collection
        ) in.readObject(); + digest = (Digest) in.readObject(); + } + + public void writeTo(DataOutputStream out) throws IOException { + out.writeByte(type); + out.writeLong(viewID); + Util.writeAddresses(flushParticipants, out); + Util.writeStreamable(digest, out); + } + + public void readFrom(DataInputStream in) throws IOException, + IllegalAccessException, + InstantiationException { + type = in.readByte(); + viewID = in.readLong(); + flushParticipants = Util.readAddresses(in, ArrayList.class); + digest = (Digest) Util.readStreamable(Digest.class, in); + } + } +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/GMS.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/GMS.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/GMS.java 17 Aug 2012 14:51:23 -0000 1.1 @@ -0,0 +1,1559 @@ + +package org.jgroups.protocols.pbcast; + + +import org.apache.commons.logging.Log; +import org.jgroups.*; +import org.jgroups.stack.Protocol; +import org.jgroups.util.*; +import org.jgroups.util.Queue; + +import java.io.*; +import java.util.*; +import java.util.concurrent.Callable; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import org.jgroups.protocols.pbcast.GmsImpl.Request; + + +/** + * Group membership protocol. Handles joins/leaves/crashes (suspicions) and emits new views + * accordingly. Use VIEW_ENFORCER on top of this layer to make sure new members don't receive + * any messages until they are members + * @author Bela Ban + * @version $Id: GMS.java,v 1.1 2012/08/17 14:51:23 marcin Exp $ + */ +public class GMS extends Protocol { + private GmsImpl impl=null; + Address local_addr=null; + final Membership members=new Membership(); // real membership + private final Membership tmp_members=new Membership(); // base for computing next view + + /** Members joined but for which no view has been received yet */ + private final Vector
        joining=new Vector
        (7); + + /** Members excluded from group, but for which no view has been received yet */ + private final Vector
        leaving=new Vector
        (7); + + View view=null; + ViewId view_id=null; + private long ltime=0; + long join_timeout=5000; + long leave_timeout=5000; + long merge_timeout=5000; // time to wait for all MERGE_RSPS + private final Object impl_mutex=new Object(); // synchronizes event entry into impl + private final Hashtable impls=new Hashtable(3); + private boolean shun=false; + boolean merge_leader=false; // can I initiate a merge ? + private boolean print_local_addr=true; + boolean disable_initial_coord=false; // can the member become a coord on startup or not ? + /** Setting this to false disables concurrent startups. This is only used by unit testing code + * for testing merging. To everybody else: don't change it to false ! */ + boolean handle_concurrent_startup=true; + /** Whether view bundling (http://jira.jboss.com/jira/browse/JGRP-144) should be enabled or not. Setting this to + * false forces each JOIN/LEAVE/SUPSECT request to be handled separately. By default these requests are processed + * together if they are queued at approximately the same time */ + private boolean view_bundling=true; + private long max_bundling_time=50; // 50ms max to wait for other JOIN, LEAVE or SUSPECT requests + static final String CLIENT="Client"; + static final String COORD="Coordinator"; + static final String PART="Participant"; + TimeScheduler timer=null; + + /** Max number of old members to keep in history */ + protected int num_prev_mbrs=50; + + /** Keeps track of old members (up to num_prev_mbrs) */ + BoundedList
        prev_members=null; + + /** If we receive a JOIN request from P and P is already in the current membership, then we send back a JOIN + * response with an error message when this property is set to true (Channel.connect() will fail). Otherwise, + * we return the current view */ + boolean reject_join_from_existing_member=true; + + int num_views=0; + + /** Stores the last 20 views */ + BoundedList prev_views=new BoundedList(20); + + + /** Class to process JOIN, LEAVE and MERGE requests */ + private final ViewHandler view_handler=new ViewHandler(); + + private Class> flushInvokerClass; + + /** To collect VIEW_ACKs from all members */ + final AckCollector ack_collector=new AckCollector(); + + //[JGRP-700] - FLUSH: flushing should span merge + final AckCollector merge_ack_collector=new AckCollector(); + + + /** Time in ms to wait for all VIEW acks (0 == wait forever) */ + long view_ack_collection_timeout=2000; + + /** How long should a Resumer wait until resuming the ViewHandler */ + long resume_task_timeout=20000; + + boolean flushProtocolInStack=false; + + public static final String name="GMS"; + + + + public GMS() { + initState(); + } + + + public String getName() { + return name; + } + + + public String getView() {return view_id != null? view_id.toString() : "null";} + public int getNumberOfViews() {return num_views;} + public String getLocalAddress() {return local_addr != null? local_addr.toString() : "null";} + public String getMembers() {return members != null? members.toString() : "[]";} + public int getNumMembers() {return members != null? members.size() : 0;} + public long getJoinTimeout() {return join_timeout;} + public void setJoinTimeout(long t) {join_timeout=t;} + /** @deprecated */ + public long getJoinRetryTimeout() {return -1;} + /** @deprecated */ + public void setJoinRetryTimeout(long t) {} + public boolean isShun() {return shun;} + public void setShun(boolean s) {shun=s;} + + public boolean isPrintLocalAddr() { + return print_local_addr; + } + + public void setPrintLocalAddr(boolean print_local_addr) { + this.print_local_addr=print_local_addr; + } + + public String printPreviousMembers() { + StringBuilder sb=new StringBuilder(); + if(prev_members != null) { + for(Address addr: prev_members) { + sb.append(addr).append("\n"); + } + } + return sb.toString(); + } + + public long getViewAckCollectionTimeout() { + return view_ack_collection_timeout; + } + + public void setViewAckCollectionTimeout(long view_ack_collection_timeout) { + this.view_ack_collection_timeout=view_ack_collection_timeout; + } + + public boolean isViewBundling() { + return view_bundling; + } + + public void setViewBundling(boolean view_bundling) { + this.view_bundling=view_bundling; + } + + public long getMaxBundlingTime() { + return max_bundling_time; + } + + public void setMaxBundlingTime(long max_bundling_time) { + this.max_bundling_time=max_bundling_time; + } + + public int viewHandlerSize() {return view_handler.size();} + public boolean isViewHandlerSuspended() {return view_handler.suspended();} + public String dumpViewHandlerQueue() { + return view_handler.dumpQueue(); + } + public String dumpViewHandlerHistory() { + return view_handler.dumpHistory(); + } + public void suspendViewHandler() { + view_handler.suspend(null); + } + public void resumeViewHandler() { + view_handler.resumeForce(); + } + + Log getLog() {return log;} + + ViewHandler getViewHandler() {return view_handler;} + + public String printPreviousViews() { + StringBuilder sb=new StringBuilder(); + for(View view: prev_views) { + sb.append(view).append("\n"); + } + return sb.toString(); + } + + public boolean isCoordinator() { + Address coord=determineCoordinator(); + return coord != null && local_addr != null && local_addr.equals(coord); + } + + + public void resetStats() { + super.resetStats(); + num_views=0; + prev_views.clear(); + } + + + public Vector requiredDownServices() { + Vector retval=new Vector(3); + retval.addElement(new Integer(Event.GET_DIGEST)); + retval.addElement(new Integer(Event.SET_DIGEST)); + retval.addElement(new Integer(Event.FIND_INITIAL_MBRS)); + return retval; + } + + public void setImpl(GmsImpl new_impl) { + synchronized(impl_mutex) { + if(impl == new_impl) // superfluous + return; + impl=new_impl; + if(log.isDebugEnabled()) { + String msg=(local_addr != null? local_addr.toString()+" " : "") + "changed role to " + new_impl.getClass().getName(); + log.debug(msg); + } + } + } + + + public GmsImpl getImpl() { + return impl; + } + + + public void init() throws Exception { + prev_members=new BoundedList
        (num_prev_mbrs); + timer=getTransport().getTimer(); + if(timer == null) + throw new Exception("GMS.init(): timer is null"); + if(impl != null) + impl.init(); + } + + public void start() throws Exception { + if(impl != null) impl.start(); + } + + public void stop() { + view_handler.stop(true); + if(impl != null) impl.stop(); + if(prev_members != null) + prev_members.clear(); + } + + + public void becomeCoordinator() { + CoordGmsImpl tmp=(CoordGmsImpl)impls.get(COORD); + if(tmp == null) { + tmp=new CoordGmsImpl(this); + impls.put(COORD, tmp); + } + try { + tmp.init(); + } + catch(Exception e) { + log.error("exception switching to coordinator role", e); + } + setImpl(tmp); + } + + + public void becomeParticipant() { + ParticipantGmsImpl tmp=(ParticipantGmsImpl)impls.get(PART); + + if(tmp == null) { + tmp=new ParticipantGmsImpl(this); + impls.put(PART, tmp); + } + try { + tmp.init(); + } + catch(Exception e) { + log.error("exception switching to participant", e); + } + setImpl(tmp); + } + + public void becomeClient() { + ClientGmsImpl tmp=(ClientGmsImpl)impls.get(CLIENT); + if(tmp == null) { + tmp=new ClientGmsImpl(this); + impls.put(CLIENT, tmp); + } + try { + tmp.init(); + } + catch(Exception e) { + log.error("exception switching to client role", e); + } + setImpl(tmp); + } + + + boolean haveCoordinatorRole() { + return impl != null && impl instanceof CoordGmsImpl; + } + + + /** + * Computes the next view. Returns a copy that has old_mbrs and + * suspected_mbrs removed and new_mbrs added. + */ + public View getNextView(Collection
        new_mbrs, Collection
        old_mbrs, Collection
        suspected_mbrs) { + Vector
        mbrs; + long vid; + View v; + Membership tmp_mbrs; + + synchronized(members) { + if(view_id == null) { + log.error("view_id is null"); + return null; // this should *never* happen ! + } + vid=Math.max(view_id.getId(), ltime) + 1; + ltime=vid; + tmp_mbrs=tmp_members.copy(); // always operate on the temporary membership + tmp_mbrs.remove(suspected_mbrs); + tmp_mbrs.remove(old_mbrs); + tmp_mbrs.add(new_mbrs); + mbrs=tmp_mbrs.getMembers(); + v=new View(local_addr, vid, mbrs); + + // Update membership (see DESIGN for explanation): + tmp_members.set(mbrs); + + // Update joining list (see DESIGN for explanation) + if(new_mbrs != null) { + for(Iterator
        it=new_mbrs.iterator(); it.hasNext();) { + Address tmp_mbr=it.next(); + if(!joining.contains(tmp_mbr)) + joining.addElement(tmp_mbr); + } + } + + // Update leaving list (see DESIGN for explanations) + if(old_mbrs != null) { + for(Iterator
        it=old_mbrs.iterator(); it.hasNext();) { + Address addr=it.next(); + if(!leaving.contains(addr)) + leaving.add(addr); + } + } + if(suspected_mbrs != null) { + for(Iterator
        it=suspected_mbrs.iterator(); it.hasNext();) { + Address addr=it.next(); + if(!leaving.contains(addr)) + leaving.add(addr); + } + } + return v; + } + } + + + /** + * Broadcasts the new view and digest, and waits for acks from all members in the list given as argument. + * If the list is null, we take the members who are part of new_view + * @param new_view + * @param digest + * @param newMembers + */ + public void castViewChangeWithDest(View new_view, Digest digest, JoinRsp jr, Collection
        newMembers) { + if(log.isTraceEnabled()) + log.trace("mcasting view {" + new_view + "} (" + new_view.size() + " mbrs)\n"); + + Message view_change_msg=new Message(); // bcast to all members + GmsHeader hdr=new GmsHeader(GmsHeader.VIEW, new_view); + hdr.my_digest=digest; + view_change_msg.putHeader(name, hdr); + + List
        ackMembers = new ArrayList
        (new_view.getMembers()); + if(newMembers != null && !newMembers.isEmpty()) { + ackMembers.removeAll(newMembers); + } + ack_collector.reset(new_view.getVid(), ackMembers); + + + // Send down a local TMP_VIEW event. This is needed by certain layers (e.g. NAKACK) to compute correct digest + // in case client's next request (e.g. getState()) reaches us *before* our own view change multicast. + // Check NAKACK's TMP_VIEW handling for details + down_prot.up(new Event(Event.TMP_VIEW, new_view)); + down_prot.down(new Event(Event.TMP_VIEW, new_view)); + down_prot.down(new Event(Event.MSG, view_change_msg)); + + try { + ack_collector.waitForAllAcks(view_ack_collection_timeout); + if(log.isTraceEnabled()) + log.trace("received all ACKs (" + ack_collector.size() + + ") for " + + new_view.getVid()); + } + catch(TimeoutException e) { + log.warn(local_addr + " failed to collect all ACKs (" + ack_collector.size() + + ") for mcasted view " + + new_view + + " after " + + view_ack_collection_timeout + + "ms, missing ACKs from " + + ack_collector.printMissing() + + " (received=" + + ack_collector.printReceived() + + "), local_addr=" + + local_addr); + } + + if(jr != null && (newMembers != null && !newMembers.isEmpty())) { + ack_collector.reset(new_view.getVid(), new ArrayList
        (newMembers)); + for(Address joiner:newMembers) { + sendJoinResponse(jr, joiner); + } + try { + ack_collector.waitForAllAcks(view_ack_collection_timeout); + if(log.isTraceEnabled()) + log.trace("received all ACKs (" + ack_collector.size() + + ") for " + + new_view.getVid()); + } + catch(TimeoutException e) { + log.warn(local_addr + " failed to collect all ACKs (" + ack_collector.size() + + ") for unicasted view " + + new_view + + " after " + + view_ack_collection_timeout + + "ms, missing ACKs from " + + ack_collector.printMissing() + + " (received=" + + ack_collector.printReceived() + + "), local_addr=" + + local_addr); + } + } + } + + public void sendJoinResponse(JoinRsp rsp, Address dest) { + Message m=new Message(dest, null, null); + GMS.GmsHeader hdr=new GMS.GmsHeader(GMS.GmsHeader.JOIN_RSP, rsp); + m.putHeader(getName(), hdr); + getDownProtocol().down(new Event(Event.ENABLE_UNICASTS_TO, dest)); + getDownProtocol().down(new Event(Event.MSG, m)); + } + + + public void installView(View new_view) { + installView(new_view, null); + } + + + /** + * Sets the new view and sends a VIEW_CHANGE event up and down the stack. If the view is a MergeView (subclass + * of View), then digest will be non-null and has to be set before installing the view. + */ + public void installView(View new_view, Digest digest) { + Address coord; + int rc; + ViewId vid=new_view.getVid(); + Vector
        mbrs=new_view.getMembers(); + + // Discards view with id lower than our own. Will be installed without check if first view + if(view_id != null) { + rc=vid.compareTo(view_id); + if(rc <= 0) { + if(log.isWarnEnabled() && rc < 0) // only scream if view is smaller, silently discard same views + log.warn("[" + local_addr + "] received view < current view;" + + " discarding it (current vid: " + view_id + ", new vid: " + vid + ')'); + return; + } + } + + if(digest != null) + mergeDigest(digest); + + if(log.isDebugEnabled()) log.debug("[local_addr=" + local_addr + "] view is " + new_view); + if(stats) { + num_views++; + prev_views.add(new_view); + } + + ack_collector.handleView(new_view); + merge_ack_collector.handleView(new_view); + + ltime=Math.max(vid.getId(), ltime); // compute Lamport logical time + + /* Check for self-inclusion: if I'm not part of the new membership, I just discard it. + This ensures that messages sent in view V1 are only received by members of V1 */ + if(checkSelfInclusion(mbrs) == false) { + // only shun if this member was previously part of the group. avoids problem where multiple + // members (e.g. X,Y,Z) join {A,B} concurrently, X is joined first, and Y and Z get view + // {A,B,X}, which would cause Y and Z to be shunned as they are not part of the membership + // bela Nov 20 2003 + if(shun && local_addr != null && prev_members.contains(local_addr)) { + if(log.isWarnEnabled()) + log.warn("I (" + local_addr + ") am not a member of view " + new_view + + ", shunning myself and leaving the group (prev_members are " + prev_members + + ", current view is " + view + ")"); + if(impl != null) + impl.handleExit(); + up_prot.up(new Event(Event.EXIT)); + } + else { + if(log.isWarnEnabled()) log.warn("I (" + local_addr + ") am not a member of view " + new_view + "; discarding view"); + } + return; + } + + synchronized(members) { // serialize access to views + // assign new_view to view_id + if(new_view instanceof MergeView) + view=new View(new_view.getVid(), new_view.getMembers()); + else + view=new_view; + view_id=vid.copy(); + + // Set the membership. Take into account joining members + if(mbrs != null && !mbrs.isEmpty()) { + members.set(mbrs); + tmp_members.set(members); + joining.removeAll(mbrs); // remove all members in mbrs from joining + // remove all elements from 'leaving' that are not in 'mbrs' + leaving.retainAll(mbrs); + + tmp_members.add(joining); // add members that haven't yet shown up in the membership + tmp_members.remove(leaving); // remove members that haven't yet been removed from the membership + + // add to prev_members + for(Iterator
        it=mbrs.iterator(); it.hasNext();) { + Address addr=it.next(); + if(!prev_members.contains(addr)) + prev_members.add(addr); + } + } + + // Send VIEW_CHANGE event up and down the stack: + Event view_event=new Event(Event.VIEW_CHANGE, new_view); + // changed order of passing view up and down (http://jira.jboss.com/jira/browse/JGRP-347) + // changed it back (bela Sept 4 2007): http://jira.jboss.com/jira/browse/JGRP-564 + down_prot.down(view_event); // needed e.g. by failure detector or UDP + up_prot.up(view_event); + + + coord=determineCoordinator(); + // if(coord != null && coord.equals(local_addr) && !(coord.equals(vid.getCoordAddress()))) { + // changed on suggestion by yaronr and Nicolas Piedeloupe + if(coord != null && coord.equals(local_addr) && !haveCoordinatorRole()) { + becomeCoordinator(); + } + else { + if(haveCoordinatorRole() && !local_addr.equals(coord)) + becomeParticipant(); + } + } + } + + + protected Address determineCoordinator() { + synchronized(members) { + return members != null && members.size() > 0? members.elementAt(0) : null; + } + } + + + /** Checks whether the potential_new_coord would be the new coordinator (2nd in line) */ + protected boolean wouldBeNewCoordinator(Address potential_new_coord) { + Address new_coord; + + if(potential_new_coord == null) return false; + + synchronized(members) { + if(members.size() < 2) return false; + new_coord=members.elementAt(1); // member at 2nd place + return new_coord != null && new_coord.equals(potential_new_coord); + } + } + + + /** Returns true if local_addr is member of mbrs, else false */ + protected boolean checkSelfInclusion(Vector
        mbrs) { + Object mbr; + if(mbrs == null) + return false; + for(int i=0; i < mbrs.size(); i++) { + mbr=mbrs.elementAt(i); + if(mbr != null && local_addr.equals(mbr)) + return true; + } + return false; + } + + + public View makeView(Vector
        mbrs) { + Address coord=null; + long id=0; + + if(view_id != null) { + coord=view_id.getCoordAddress(); + id=view_id.getId(); + } + return new View(coord, id, mbrs); + } + + + public static View makeView(Vector
        mbrs, ViewId vid) { + Address coord=null; + long id=0; + + if(vid != null) { + coord=vid.getCoordAddress(); + id=vid.getId(); + } + return new View(coord, id, mbrs); + } + + + /** Send down a SET_DIGEST event */ + public void setDigest(Digest d) { + down_prot.down(new Event(Event.SET_DIGEST, d)); + } + + + /** Send down a MERGE_DIGEST event */ + public void mergeDigest(Digest d) { + down_prot.down(new Event(Event.MERGE_DIGEST, d)); + } + + + /** Sends down a GET_DIGEST event and waits for the GET_DIGEST_OK response, or + timeout, whichever occurs first */ + public Digest getDigest() { + return (Digest)down_prot.down(Event.GET_DIGEST_EVT); + } + + boolean startFlush(final View new_view) { + if(flushInvokerClass == null){ + Callable invoker = new Callable(){ + public Boolean call() throws Exception { + int maxAttempts =4; + long randomFloor=1000L; + long randomCeiling=5000L; + + boolean successfulFlush=true; + boolean validView=new_view != null && new_view.size() > 0; + if(validView && flushProtocolInStack) { + + int attemptCount = 0; + while(attemptCount < maxAttempts){ + successfulFlush=(Boolean)up_prot.up(new Event(Event.SUSPEND, new ArrayList
        (new_view.getMembers()))); + if(successfulFlush) + break; + Util.sleepRandom(randomFloor,randomCeiling); + attemptCount++; + } + + if(successfulFlush) { + if(log.isTraceEnabled()) + log.trace("Successful GMS flush by coordinator at " + getLocalAddress()); + } + else { + if(log.isWarnEnabled()) + log.warn("GMS flush by coordinator at " + getLocalAddress() + " failed"); + } + } + return successfulFlush; + } + }; + try { + return invoker.call(); + } catch (Exception e) { + return false; + } + } + else{ + Callable invoker = null; + try { + invoker = flushInvokerClass.getDeclaredConstructor(View.class).newInstance(new_view); + return invoker.call(); + } catch (Exception e) { + return false; + } + } + } + + void stopFlush() { + if(flushProtocolInStack) { + if(log.isDebugEnabled()) { + log.debug(getLocalAddress() + " sending RESUME event"); + } + up_prot.up(new Event(Event.RESUME)); + } + } + + void stopFlush(List
        members) { + + if(log.isDebugEnabled()){ + log.debug(getLocalAddress() + " sending RESUME event"); + } + up_prot.up(new Event(Event.RESUME,members)); + } + + + public Object up(Event evt) { + switch(evt.getType()) { + + case Event.MSG: + Message msg=(Message)evt.getArg(); + GmsHeader hdr=(GmsHeader)msg.getHeader(name); + if(hdr == null) + break; + switch(hdr.type) { + case GmsHeader.JOIN_REQ: + view_handler.add(new Request(Request.JOIN, hdr.mbr, false, null)); + break; + case GmsHeader.JOIN_REQ_WITH_STATE_TRANSFER: + view_handler.add(new Request(Request.JOIN_WITH_STATE_TRANSFER, hdr.mbr, false, null)); + break; + case GmsHeader.JOIN_RSP: + impl.handleJoinResponse(hdr.join_rsp); + break; + case GmsHeader.LEAVE_REQ: + if(log.isDebugEnabled()) + log.debug("received LEAVE_REQ for " + hdr.mbr + " from " + msg.getSrc()); + if(hdr.mbr == null) { + if(log.isErrorEnabled()) log.error("LEAVE_REQ's mbr field is null"); + return null; + } + view_handler.add(new Request(Request.LEAVE, hdr.mbr, false, null)); + break; + case GmsHeader.LEAVE_RSP: + impl.handleLeaveResponse(); + break; + case GmsHeader.VIEW: + View new_view=hdr.view; + if(new_view == null) { + if(log.isErrorEnabled()) log.error("[VIEW]: view == null"); + return null; + } + + Address coord=msg.getSrc(); + if(!new_view.containsMember(coord)) { + sendViewAck(coord); // we need to send the ack first, otherwise the connection is removed + impl.handleViewChange(new_view, hdr.my_digest); + } + else { + impl.handleViewChange(new_view, hdr.my_digest); + sendViewAck(coord); // send VIEW_ACK to sender of view + } + break; + + case GmsHeader.VIEW_ACK: + Address sender=msg.getSrc(); + ack_collector.ack(sender); + return null; // don't pass further up + + case GmsHeader.MERGE_REQ: + down_prot.down(new Event(Event.SUSPEND_STABLE, 20000)); + + if(log.isDebugEnabled()){ + log.debug("Merge participant " + local_addr + " got merge request from " + msg.getSrc()); + } + impl.handleMergeRequest(msg.getSrc(), hdr.merge_id); + break; + + case GmsHeader.MERGE_RSP: + MergeData merge_data=new MergeData(msg.getSrc(), hdr.view, hdr.my_digest); + merge_data.merge_rejected=hdr.merge_rejected; + if(log.isDebugEnabled()) { + log.debug("Got merge response at " + local_addr + " from " + msg.getSrc() + + ", merge_id=" + hdr.view+ ", merge data is "+ merge_data); + } + impl.handleMergeResponse(merge_data, hdr.merge_id); + break; + + case GmsHeader.INSTALL_MERGE_VIEW: + impl.handleMergeView(new MergeData(msg.getSrc(), hdr.view, hdr.my_digest), hdr.merge_id); + down_prot.down(new Event(Event.RESUME_STABLE)); + break; + + case GmsHeader.INSTALL_MERGE_VIEW_OK: + //[JGRP-700] - FLUSH: flushing should span merge + merge_ack_collector.ack(msg.getSrc()); + break; + + case GmsHeader.CANCEL_MERGE: + //[JGRP-524] - FLUSH and merge: flush doesn't wrap entire merge process + impl.handleMergeCancelled(hdr.merge_id); + down_prot.down(new Event(Event.RESUME_STABLE)); + break; + + default: + if(log.isErrorEnabled()) log.error("GmsHeader with type=" + hdr.type + " not known"); + } + return null; // don't pass up + + case Event.SET_LOCAL_ADDRESS: + local_addr=(Address)evt.getArg(); + break; // pass up + + case Event.SUSPECT: + Address suspected=(Address)evt.getArg(); + view_handler.add(new Request(Request.SUSPECT, suspected, true, null)); + ack_collector.suspect(suspected); + merge_ack_collector.suspect(suspected); + break; // pass up + + case Event.UNSUSPECT: + impl.unsuspect((Address)evt.getArg()); + return null; // discard + + case Event.MERGE: + view_handler.add(new Request(Request.MERGE, null, false, (Vector
        )evt.getArg())); + return null; // don't pass up + } + + if(impl.handleUpEvent(evt)) + return up_prot.up(evt); + return null; + } + + + + + + /** + This method is overridden to avoid hanging on getDigest(): when a JOIN is received, the coordinator needs + to retrieve the digest from the NAKACK layer. It therefore sends down a GET_DIGEST event, to which the NAKACK layer + responds with a GET_DIGEST_OK event.

        + However, the GET_DIGEST_OK event will not be processed because the thread handling the JOIN request won't process + the GET_DIGEST_OK event until the JOIN event returns. The receiveUpEvent() method is executed by the up-handler + thread of the lower protocol and therefore can handle the event. All we do here is unblock the mutex on which + JOIN is waiting, allowing JOIN to return with a valid digest. The GET_DIGEST_OK event is then discarded, because + it won't be processed twice. + */ +// public void receiveUpEvent(Event evt) { +// switch(evt.getType()) { +// case Event.GET_DIGEST_OK: +// digest_promise.setResult(evt.getArg()); +// return; // don't pass further up +// } +// super.receiveUpEvent(evt); +// } + + + public Object down(Event evt) { + Object arg=null; + switch(evt.getType()) { + case Event.CONNECT: + if(print_local_addr) { + System.out.println("\n---------------------------------------------------------\n" + + "GMS: address is " + local_addr + " (cluster=" + evt.getArg() + ")" + + "\n---------------------------------------------------------"); + } + down_prot.down(evt); + if(local_addr == null) + if(log.isFatalEnabled()) log.fatal("[CONNECT] local_addr is null"); + try { + impl.join(local_addr); + } + catch(Throwable e) { + arg=e; + } + return arg; // don't pass down: was already passed down + + case Event.CONNECT_WITH_STATE_TRANSFER: + if(print_local_addr) { + System.out.println("\n---------------------------------------------------------\n" + + "GMS: address is " + local_addr + " (cluster=" + evt.getArg() + ")" + + "\n---------------------------------------------------------"); + } + down_prot.down(evt); + if(local_addr == null) + if(log.isFatalEnabled()) log.fatal("[CONNECT] local_addr is null"); + try { + impl.joinWithStateTransfer(local_addr); + } + catch(Throwable e) { + arg=e; + } + return arg; // don't pass down: was already passed down + + case Event.DISCONNECT: + impl.leave((Address)evt.getArg()); + if(!(impl instanceof CoordGmsImpl)) { + initState(); // in case connect() is called again + } + down_prot.down(evt); // notify the other protocols, but ignore the result + return null; + + case Event.CONFIG : + Map config=(Map)evt.getArg(); + if((config != null && config.containsKey("flush_supported"))){ + flushProtocolInStack=true; + } + break; + } + + return down_prot.down(evt); + } + + + /** Setup the Protocol instance according to the configuration string */ + public boolean setProperties(Properties props) { + String str; + + super.setProperties(props); + str=props.getProperty("shun"); + if(str != null) { + shun=Boolean.valueOf(str).booleanValue(); + props.remove("shun"); + } + + str=props.getProperty("merge_leader"); + if(str != null) { + merge_leader=Boolean.valueOf(str).booleanValue(); + props.remove("merge_leader"); + } + + str=props.getProperty("print_local_addr"); + if(str != null) { + print_local_addr=Boolean.valueOf(str).booleanValue(); + props.remove("print_local_addr"); + } + + str=props.getProperty("join_timeout"); // time to wait for JOIN + if(str != null) { + join_timeout=Long.parseLong(str); + props.remove("join_timeout"); + } + + str=props.getProperty("join_retry_timeout"); // time to wait between JOINs + if(str != null) { + props.remove("join_retry_timeout"); + if(log.isWarnEnabled()) + log.warn("join_retry_timeout has been deprecated and its value will be ignored"); + } + + str=props.getProperty("leave_timeout"); // time to wait until coord responds to LEAVE req. + if(str != null) { + leave_timeout=Long.parseLong(str); + props.remove("leave_timeout"); + } + + str=props.getProperty("merge_timeout"); // time to wait for MERGE_RSPS from subgroup coordinators + if(str != null) { + merge_timeout=Long.parseLong(str); + props.remove("merge_timeout"); + } + + str=props.getProperty("digest_timeout"); // time to wait for GET_DIGEST_OK from PBCAST + if(str != null) { + log.warn("digest_timeout has been deprecated and its value will be ignored"); + props.remove("digest_timeout"); + } + + str=props.getProperty("view_ack_collection_timeout"); + if(str != null) { + view_ack_collection_timeout=Long.parseLong(str); + props.remove("view_ack_collection_timeout"); + } + + str=props.getProperty("resume_task_timeout"); + if(str != null) { + resume_task_timeout=Long.parseLong(str); + props.remove("resume_task_timeout"); + } + + str=props.getProperty("disable_initial_coord"); + if(str != null) { + disable_initial_coord=Boolean.valueOf(str).booleanValue(); + props.remove("disable_initial_coord"); + if(log.isWarnEnabled()) + log.warn("disable_initial_coord has been deprecated and will be phased out by 3.0, please don't use it anymore"); + } + + str=props.getProperty("handle_concurrent_startup"); + if(str != null) { + handle_concurrent_startup=Boolean.valueOf(str).booleanValue(); + props.remove("handle_concurrent_startup"); + } + + str=props.getProperty("num_prev_mbrs"); + if(str != null) { + num_prev_mbrs=Integer.parseInt(str); + props.remove("num_prev_mbrs"); + } + + str=props.getProperty("reject_join_from_existing_member"); + if(str != null) { + reject_join_from_existing_member=Boolean.parseBoolean(str); + props.remove("reject_join_from_existing_member"); + } + + str=props.getProperty("use_flush"); + if(str != null) { + log.warn("use_flush has been deprecated and its value will be ignored"); + props.remove("use_flush"); + } + str=props.getProperty("flush_timeout"); + if(str != null) { + log.warn("flush_timeout has been deprecated and its value will be ignored"); + props.remove("flush_timeout"); + } + + str=props.getProperty("view_bundling"); + if(str != null) { + view_bundling=Boolean.valueOf(str).booleanValue(); + props.remove("view_bundling"); + } + + str=props.getProperty("max_bundling_time"); + if(str != null) { + max_bundling_time=Long.parseLong(str); + props.remove("max_bundling_time"); + } + str=props.getProperty("flush_invoker_class"); + if (str != null) { + try { + flushInvokerClass = (Class>) Class.forName(str); + flushInvokerClass.getDeclaredConstructor(View.class); + } catch (Exception e) { + log.error("Invalid flush invoker class " + + str + + ". Maker sure it implements > with public constructor using View as a parameter"); + } + props.remove("flush_invoker_class"); + } + + + if(!props.isEmpty()) { + log.error("the following properties are not recognized: " + props); + return false; + } + return true; + } + + + + /* ------------------------------- Private Methods --------------------------------- */ + + final void initState() { + becomeClient(); + view_id=null; + view=null; + } + + + private void sendViewAck(Address dest) { + Message view_ack=new Message(dest, null, null); + view_ack.setFlag(Message.OOB); + GmsHeader tmphdr=new GmsHeader(GmsHeader.VIEW_ACK); + view_ack.putHeader(name, tmphdr); + if(log.isTraceEnabled()) + log.trace("sending VIEW_ACK to " + dest); + down_prot.down(new Event(Event.MSG, view_ack)); + } + + /* --------------------------- End of Private Methods ------------------------------- */ + + + + public static class GmsHeader extends Header implements Streamable { + public static final byte JOIN_REQ=1; + public static final byte JOIN_RSP=2; + public static final byte LEAVE_REQ=3; + public static final byte LEAVE_RSP=4; + public static final byte VIEW=5; + public static final byte MERGE_REQ=6; + public static final byte MERGE_RSP=7; + public static final byte INSTALL_MERGE_VIEW=8; + public static final byte CANCEL_MERGE=9; + public static final byte VIEW_ACK=10; + public static final byte JOIN_REQ_WITH_STATE_TRANSFER = 11; + public static final byte INSTALL_MERGE_VIEW_OK=12; + + byte type=0; + View view=null; // used when type=VIEW or MERGE_RSP or INSTALL_MERGE_VIEW + Address mbr=null; // used when type=JOIN_REQ or LEAVE_REQ + JoinRsp join_rsp=null; // used when type=JOIN_RSP + Digest my_digest=null; // used when type=MERGE_RSP or INSTALL_MERGE_VIEW + ViewId merge_id=null; // used when type=MERGE_REQ or MERGE_RSP or INSTALL_MERGE_VIEW or CANCEL_MERGE + boolean merge_rejected=false; // used when type=MERGE_RSP + private static final long serialVersionUID=2369798797842183276L; + + + public GmsHeader() { + } // used for Externalization + + public GmsHeader(byte type) { + this.type=type; + } + + + /** Used for VIEW header */ + public GmsHeader(byte type, View view) { + this.type=type; + this.view=view; + } + + + /** Used for JOIN_REQ or LEAVE_REQ header */ + public GmsHeader(byte type, Address mbr) { + this.type=type; + this.mbr=mbr; + } + + /** Used for JOIN_RSP header */ + public GmsHeader(byte type, JoinRsp join_rsp) { + this.type=type; + this.join_rsp=join_rsp; + } + + public byte getType() { + return type; + } + + public Address getMember() { + return mbr; + } + + public String toString() { + StringBuilder sb=new StringBuilder("GmsHeader"); + sb.append('[' + type2String(type) + ']'); + switch(type) { + case JOIN_REQ: + sb.append(": mbr=" + mbr); + break; + + case JOIN_RSP: + sb.append(": join_rsp=" + join_rsp); + break; + + case LEAVE_REQ: + sb.append(": mbr=" + mbr); + break; + + case LEAVE_RSP: + break; + + case VIEW: + case VIEW_ACK: + sb.append(": view=" + view); + break; + + case MERGE_REQ: + sb.append(": merge_id=" + merge_id); + break; + + case MERGE_RSP: + sb.append(": view=" + view + ", digest=" + my_digest + ", merge_rejected=" + merge_rejected + + ", merge_id=" + merge_id); + break; + + case INSTALL_MERGE_VIEW: + sb.append(": view=" + view + ", digest=" + my_digest); + break; + + case CANCEL_MERGE: + sb.append(", , merge_id=" + merge_id); + break; + } + return sb.toString(); + } + + + public static String type2String(int type) { + switch(type) { + case JOIN_REQ: return "JOIN_REQ"; + case JOIN_RSP: return "JOIN_RSP"; + case LEAVE_REQ: return "LEAVE_REQ"; + case LEAVE_RSP: return "LEAVE_RSP"; + case VIEW: return "VIEW"; + case MERGE_REQ: return "MERGE_REQ"; + case MERGE_RSP: return "MERGE_RSP"; + case INSTALL_MERGE_VIEW: return "INSTALL_MERGE_VIEW"; + case CANCEL_MERGE: return "CANCEL_MERGE"; + case VIEW_ACK: return "VIEW_ACK"; + case JOIN_REQ_WITH_STATE_TRANSFER: return "JOIN_REQ_WITH_STATE_TRANSFER"; + default: return ""; + } + } + + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeByte(type); + out.writeObject(view); + out.writeObject(mbr); + out.writeObject(join_rsp); + out.writeObject(my_digest); + out.writeObject(merge_id); + out.writeBoolean(merge_rejected); + } + + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + type=in.readByte(); + view=(View)in.readObject(); + mbr=(Address)in.readObject(); + join_rsp=(JoinRsp)in.readObject(); + my_digest=(Digest)in.readObject(); + merge_id=(ViewId)in.readObject(); + merge_rejected=in.readBoolean(); + } + + + public void writeTo(DataOutputStream out) throws IOException { + out.writeByte(type); + boolean isMergeView=view != null && view instanceof MergeView; + out.writeBoolean(isMergeView); + Util.writeStreamable(view, out); + Util.writeAddress(mbr, out); + Util.writeStreamable(join_rsp, out); + Util.writeStreamable(my_digest, out); + Util.writeStreamable(merge_id, out); // kludge: we know merge_id is a ViewId + out.writeBoolean(merge_rejected); + } + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + type=in.readByte(); + boolean isMergeView=in.readBoolean(); + if(isMergeView) + view=(View)Util.readStreamable(MergeView.class, in); + else + view=(View)Util.readStreamable(View.class, in); + mbr=Util.readAddress(in); + join_rsp=(JoinRsp)Util.readStreamable(JoinRsp.class, in); + my_digest=(Digest)Util.readStreamable(Digest.class, in); + merge_id=(ViewId)Util.readStreamable(ViewId.class, in); + merge_rejected=in.readBoolean(); + } + + public int size() { + int retval=Global.BYTE_SIZE *2; // type + merge_rejected + + retval+=Global.BYTE_SIZE; // presence view + retval+=Global.BYTE_SIZE; // MergeView or View + if(view != null) + retval+=view.serializedSize(); + + retval+=Util.size(mbr); + + retval+=Global.BYTE_SIZE; // presence of join_rsp + if(join_rsp != null) + retval+=join_rsp.serializedSize(); + + retval+=Global.BYTE_SIZE; // presence for my_digest + if(my_digest != null) + retval+=my_digest.serializedSize(); + + retval+=Global.BYTE_SIZE; // presence for merge_id + if(merge_id != null) + retval+=merge_id.serializedSize(); + return retval; + } + + } + + + + + + + + + + /** + * Class which processes JOIN, LEAVE and MERGE requests. Requests are queued and processed in FIFO order + * @author Bela Ban + * @version $Id: GMS.java,v 1.1 2012/08/17 14:51:23 marcin Exp $ + */ + class ViewHandler implements Runnable { + volatile Thread thread; + Queue q=new Queue(); // Queue + boolean suspended=false; + final static long INTERVAL=5000; + private static final long MAX_COMPLETION_TIME=10000; + /** Maintains a list of the last 20 requests */ + private final BoundedList history=new BoundedList(20); + + /** Map. Keeps track of Resumer tasks which have not fired yet */ + private final Map resume_tasks=new HashMap(); + private Object merge_id=null; + + + void add(Request req) { + add(req, false, false); + } + + synchronized void add(Request req, boolean at_head, boolean unsuspend) { + if(suspended && !unsuspend) { + log.warn("queue is suspended; request " + req + " is discarded"); + return; + } + start(unsuspend); + try { + if(at_head) + q.addAtHead(req); + else + q.add(req); + history.add(new Date() + ": " + req.toString()); + } + catch(QueueClosedException e) { + if(log.isTraceEnabled()) + log.trace("queue is closed; request " + req + " is discarded"); + } + } + + + void waitUntilCompleted(long timeout) { + waitUntilCompleted(timeout, false); + } + + synchronized void waitUntilCompleted(long timeout, boolean resume) { + if(thread != null) { + try { + thread.join(timeout); + } + catch(InterruptedException e) { + Thread.currentThread().interrupt(); // set interrupt flag again + } + //Added after Brian noticed that ViewHandler leaks class loaders + thread = null; + } + if(resume) + resumeForce(); + } + + /** + * Waits until the current request has been processes, then clears the queue and discards new + * requests from now on + */ + public synchronized void suspend(Object merge_id) { + if(!suspended) { + suspended=true; + this.merge_id=merge_id; + q.clear(); + waitUntilCompleted(MAX_COMPLETION_TIME); + q.close(true); + + if(log.isDebugEnabled()) + log.debug("suspended ViewHandler at " + local_addr); + Resumer resumer=new Resumer(merge_id, resume_tasks, this); + Future future=timer.schedule(resumer, resume_task_timeout, TimeUnit.MILLISECONDS); + Future old_future=resume_tasks.put(merge_id, future); + if(old_future != null) + old_future.cancel(true); + } + else { + if(log.isWarnEnabled()) { + log.warn("attempted suspend on ViewHandler at " + local_addr+ ", however, it is already suspended"); + } + } + } + + + public synchronized void resume(Object merge_id) { + if(suspended) { + boolean same_merge_id=this.merge_id != null && merge_id != null && this.merge_id.equals(merge_id); + same_merge_id=same_merge_id || (this.merge_id == null && merge_id == null); + + if(same_merge_id) { + synchronized(resume_tasks) { + Future future=resume_tasks.get(merge_id); + if(future != null) { + future.cancel(false); + resume_tasks.remove(merge_id); + } + } + } + else{ + if(log.isWarnEnabled()) + log.warn("resume(" + merge_id+ ") does not match "+ this.merge_id); + } + resumeForce(); + } + } + + public synchronized void resumeForce() { + if(q.closed()) + q.reset(); + suspended=false; + if(log.isTraceEnabled()) + log.trace("resumed ViewHandler"); + } + + public void run() { + long end_time, wait_time; + List requests=new LinkedList(); + while(Thread.currentThread().equals(thread) && !suspended) { + try { + boolean keepGoing=false; + end_time=System.currentTimeMillis() + max_bundling_time; + do { + Request firstRequest=(Request)q.remove(INTERVAL); // throws a TimeoutException if it runs into timeout + requests.add(firstRequest); + if(!view_bundling) + break; + if(q.size() > 0) { + Request nextReq=(Request)q.peek(); + keepGoing=view_bundling && firstRequest.canBeProcessedTogether(nextReq); + } + else { + wait_time=end_time - System.currentTimeMillis(); + if(wait_time > 0) + q.waitUntilClosed(wait_time); // misnomer: waits until element has been added or q closed + keepGoing=q.size() > 0 && firstRequest.canBeProcessedTogether((Request)q.peek()); + } + } + while(keepGoing && System.currentTimeMillis() < end_time); + + try { + process(requests); + } + finally { + requests.clear(); + } + } + catch(QueueClosedException e) { + break; + } + catch(TimeoutException e) { + break; + } + catch(Throwable catchall) { + Util.sleep(50); + } + } + } + + public int size() {return q.size();} + public boolean suspended() {return suspended;} + public String dumpQueue() { + StringBuilder sb=new StringBuilder(); + List v=q.values(); + for(Iterator it=v.iterator(); it.hasNext();) { + sb.append(it.next() + "\n"); + } + return sb.toString(); + } + + public String dumpHistory() { + StringBuilder sb=new StringBuilder(); + for(String line: history) { + sb.append(line + "\n"); + } + return sb.toString(); + } + + private void process(List requests) { + if(requests.isEmpty()) + return; + if(log.isTraceEnabled()) + log.trace("processing " + requests); + Request firstReq=requests.get(0); + switch(firstReq.type) { + case Request.JOIN: + case Request.JOIN_WITH_STATE_TRANSFER: + case Request.LEAVE: + case Request.SUSPECT: + impl.handleMembershipChange(requests); + break; + case Request.MERGE: + if(requests.size() > 1) + log.error("more than one MERGE request to process, ignoring the others"); + impl.merge(firstReq.coordinators); + break; + default: + log.error("request " + firstReq.type + " is unknown; discarded"); + } + } + + + + + synchronized void start(boolean unsuspend) { + if(q.closed()) + q.reset(); + if(unsuspend) { + suspended=false; + Future future; + synchronized(resume_tasks) { + future=resume_tasks.remove(merge_id); + } + if(future != null) + future.cancel(true); + } + merge_id=null; + if(thread == null || !thread.isAlive()) { + thread=getThreadFactory().newThread(this, "ViewHandler"); + thread.setDaemon(false); // thread cannot terminate if we have tasks left, e.g. when we as coord leave + thread.start(); + } + } + + synchronized void stop(boolean flush) { + q.close(flush); + synchronized(resume_tasks) { + for(Future future: resume_tasks.values()) { + future.cancel(true); + } + resume_tasks.clear(); + } + merge_id=null; + // resumeForce(); + + // commented as this is already done in waitUnitlCompleted() + // thread = null; // Workaround for potential classloader leak to our thread + } + } + + + /** + * Resumer is a second line of defense: when the ViewHandler is suspended, it will be resumed when the current + * merge is cancelled, or when the merge completes. However, in a case where this never happens (this + * shouldn't be the case !), the Resumer will nevertheless resume the ViewHandler. + * We chose this strategy because ViewHandler is critical: if it is suspended indefinitely, we would + * not be able to process new JOIN requests ! So, this is for peace of mind, although it most likely + * will never be used... + */ + static class Resumer implements Runnable { + final Object token; + final Map tasks; + final ViewHandler handler; + + + public Resumer(final Object token, final Map t, final ViewHandler handler) { + this.token=token; + this.tasks=t; + this.handler=handler; + } + + public void run() { + boolean execute=true; + synchronized(tasks) { + Future future=tasks.get(token); + if(future != null) { + future.cancel(false); + execute=true; + } + else { + execute=false; + } + tasks.remove(token); + } + if(execute) { + handler.resume(token); + } + } + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/GmsImpl.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/GmsImpl.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/GmsImpl.java 17 Aug 2012 14:51:22 -0000 1.1 @@ -0,0 +1,149 @@ +// $Id: GmsImpl.java,v 1.1 2012/08/17 14:51:22 marcin Exp $ + +package org.jgroups.protocols.pbcast; + +import org.apache.commons.logging.Log; +import org.jgroups.*; +import org.jgroups.util.Digest; + +import java.util.Collection; +import java.util.Vector; +import java.util.List; + + +public abstract class GmsImpl { + protected GMS gms=null; + protected final Log log; + final boolean trace; + final boolean warn; + volatile boolean leaving=false; + + protected GmsImpl() { + log=null; + trace=warn=false; + } + + protected GmsImpl(GMS gms) { + this.gms=gms; + log=gms.getLog(); + trace=log.isTraceEnabled(); + warn=log.isWarnEnabled(); + } + + public abstract void join(Address mbr); + public abstract void joinWithStateTransfer(Address local_addr); + + public abstract void leave(Address mbr); + + public abstract void handleJoinResponse(JoinRsp join_rsp); + public abstract void handleLeaveResponse(); + + public abstract void suspect(Address mbr); + public abstract void unsuspect(Address mbr); + + public void merge(Vector

        other_coords) {} // only processed by coord + public void handleMergeRequest(Address sender, ViewId merge_id) {} // only processed by coords + public void handleMergeResponse(MergeData data, ViewId merge_id) {} // only processed by coords + public void handleMergeView(MergeData data, ViewId merge_id) {} // only processed by coords + public void handleMergeCancelled(ViewId merge_id) {} // only processed by coords + + public abstract void handleMembershipChange(Collection requests); + public abstract void handleViewChange(View new_view, Digest digest); + public void handleExit() {} + + public boolean handleUpEvent(Event evt) {return true;} + + public void init() throws Exception {leaving=false;} + public void start() throws Exception {leaving=false;} + public void stop() {leaving=true;} + + + + protected void sendMergeRejectedResponse(Address sender, ViewId merge_id) { + Message msg=new Message(sender, null, null); + msg.setFlag(Message.OOB); + GMS.GmsHeader hdr=new GMS.GmsHeader(GMS.GmsHeader.MERGE_RSP); + hdr.merge_rejected=true; + hdr.merge_id=merge_id; + msg.putHeader(gms.getName(), hdr); + if(log.isDebugEnabled()) log.debug("response=" + hdr); + gms.getDownProtocol().down(new Event(Event.ENABLE_UNICASTS_TO, sender)); + gms.getDownProtocol().down(new Event(Event.MSG, msg)); + } + + + protected void wrongMethod(String method_name) { + if(log.isWarnEnabled()) + log.warn(method_name + "() should not be invoked on an instance of " + getClass().getName()); + } + + + + /** + Returns potential coordinator based on lexicographic ordering of member addresses. Another + approach would be to keep track of the primary partition and return the first member if we + are the primary partition. + */ + protected boolean iWouldBeCoordinator(Vector new_mbrs) { + Membership tmp_mbrs=gms.members.copy(); + tmp_mbrs.merge(new_mbrs, null); + tmp_mbrs.sort(); + return !(tmp_mbrs.size() <= 0 || gms.local_addr == null) && gms.local_addr.equals(tmp_mbrs.elementAt(0)); + } + + + public static class Request { + static final int JOIN = 1; + static final int LEAVE = 2; + static final int SUSPECT = 3; + static final int MERGE = 4; + static final int JOIN_WITH_STATE_TRANSFER = 6; + + + int type=-1; + Address mbr; + boolean suspected; + Vector
        coordinators; + View view; + Digest digest; + List
        target_members; + + Request(int type) { + this.type=type; + } + + Request(int type, Address mbr, boolean suspected, Vector
        coordinators) { + this.type=type; + this.mbr=mbr; + this.suspected=suspected; + this.coordinators=coordinators; + } + + public int getType() { + return type; + } + + public String toString() { + switch(type) { + case JOIN: return "JOIN(" + mbr + ")"; + case JOIN_WITH_STATE_TRANSFER: return "JOIN_WITH_STATE_TRANSFER(" + mbr + ")"; + case LEAVE: return "LEAVE(" + mbr + ", " + suspected + ")"; + case SUSPECT: return "SUSPECT(" + mbr + ")"; + case MERGE: return "MERGE(" + coordinators + ")"; + } + return ""); + if(digest != null) sb.append(", digest=" + digest); + if(not_seen != null) sb.append(", not_seen=" + not_seen); + if(seen != null) sb.append(", seen=" + seen); + sb.append(", id=" + id); + return sb.toString(); + } + + + public String shortForm() { + StringBuilder sb=new StringBuilder(); + if(sender != null) + sb.append(sender); + else + sb.append(""); + sb.append("#" + id); + return sb.toString(); + } + + + public static void main(String[] args) throws UnknownHostException { + Gossip id1, id2; + + id1=new Gossip(new org.jgroups.stack.IpAddress("daddy", 4567), 23); + id2=new Gossip(new org.jgroups.stack.IpAddress("133.164.130.19", 4567), 23); + + System.out.println(id1.equals(id2)); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/JoinRsp.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/JoinRsp.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/JoinRsp.java 17 Aug 2012 14:51:22 -0000 1.1 @@ -0,0 +1,101 @@ +// $Id: JoinRsp.java,v 1.1 2012/08/17 14:51:22 marcin Exp $ + +package org.jgroups.protocols.pbcast; + + +import org.jgroups.View; +import org.jgroups.Global; +import org.jgroups.util.Streamable; +import org.jgroups.util.Util; +import org.jgroups.util.Digest; + +import java.io.Serializable; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.DataInputStream; + + +/** + * Result of a JOIN request (sent by the GMS client). Instances of this class are immutable. + */ +public class JoinRsp implements Serializable, Streamable { + private View view=null; + private Digest digest=null; + /** only set if JOIN failed, e.g. in AUTH */ + private String fail_reason=null; + private static final long serialVersionUID = -212620440767943314L; + + + + public JoinRsp() { + + } + + public JoinRsp(View v, Digest d) { + view=v; + digest=d; + } + + public JoinRsp(String fail_reason) { + this.fail_reason=fail_reason; + } + + public View getView() { + return view; + } + + public Digest getDigest() { + return digest; + } + + public String getFailReason() { + return fail_reason; + } + + public void setFailReason(String r) { + fail_reason=r; + } + + + public void writeTo(DataOutputStream out) throws IOException { + Util.writeStreamable(view, out); + Util.writeStreamable(digest, out); + Util.writeString(fail_reason, out); + } + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + view=(View)Util.readStreamable(View.class, in); + digest=(Digest)Util.readStreamable(Digest.class, in); + fail_reason=Util.readString(in); + } + + public int serializedSize() { + int retval=Global.BYTE_SIZE * 2; // presence for view and digest + if(view != null) + retval+=view.serializedSize(); + if(digest != null) + retval+=digest.serializedSize(); + + retval+=Global.BYTE_SIZE; // presence byte for fail_reason + if(fail_reason != null) + retval+=fail_reason.length() +2; + return retval; + } + + public String toString() { + StringBuilder sb=new StringBuilder(); + sb.append("view: "); + if(view == null) + sb.append(""); + else + sb.append(view); + sb.append(", digest: "); + if(digest == null) + sb.append(""); + else + sb.append(digest); + if(fail_reason != null) + sb.append(", fail reason: ").append(fail_reason); + return sb.toString(); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/MergeData.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/MergeData.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/MergeData.java 17 Aug 2012 14:51:22 -0000 1.1 @@ -0,0 +1,101 @@ +// $Id: MergeData.java,v 1.1 2012/08/17 14:51:22 marcin Exp $ + +package org.jgroups.protocols.pbcast; + + +import org.jgroups.Address; +import org.jgroups.View; +import org.jgroups.util.Digest; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; + + +/** + * Encapsulates data sent with a MERGE_RSP (handleMergeResponse()) and INSTALL_MERGE_VIEW + * (handleMergeView()). + * + * @author Bela Ban Oct 22 2001 + */ +public class MergeData implements Externalizable { + Address sender=null; + boolean merge_rejected=false; + View view=null; + Digest digest=null; + + /** + * Empty constructor needed for externalization + */ + public MergeData() { + } + + public MergeData(Address sender, View view, Digest digest) { + this.sender=sender; + this.view=view; + this.digest=digest; + } + + public Address getSender() { + return sender; + } + + public View getView() { + return view; + } + + public Digest getDigest() { + return digest; + } + + public void setView(View v) { + view=v; + } + + public void setDigest(Digest d) { + digest=d; + } + + + public boolean equals(Object other) { + return sender != null && other != null && other instanceof MergeData && + ((MergeData)other).sender != null && ((MergeData)other).sender.equals(sender); + } + + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeObject(sender); + out.writeBoolean(merge_rejected); + if(!merge_rejected) { + out.writeObject(view); + out.writeObject(digest); + } + } + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + sender=(Address)in.readObject(); + merge_rejected=in.readBoolean(); + if(!merge_rejected) { + view=(View)in.readObject(); + digest=(Digest)in.readObject(); + } + } + + + public String toString() { + StringBuilder sb=new StringBuilder(); + sb.append("sender=").append(sender); + if(merge_rejected) + sb.append(" (merge_rejected)"); + else { + sb.append(", view=").append(view).append(", digest=").append(digest); + } + return sb.toString(); + } + + +} + + + Index: 3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/NAKACK.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/NAKACK.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/NAKACK.java 17 Aug 2012 14:51:22 -0000 1.1 @@ -0,0 +1,1889 @@ +package org.jgroups.protocols.pbcast; + +import org.jgroups.Address; +import org.jgroups.Event; +import org.jgroups.Message; +import org.jgroups.View; +import org.jgroups.annotations.GuardedBy; +import org.jgroups.stack.*; +import org.jgroups.util.*; + +import java.io.FileWriter; +import java.io.IOException; +import java.io.Writer; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + + +/** + * Negative AcKnowledgement layer (NAKs). Messages are assigned a monotonically increasing sequence number (seqno). + * Receivers deliver messages ordered according to seqno and request retransmission of missing messages.
        + * Retransmit requests are usually sent to the original sender of a message, but this can be changed by + * xmit_from_random_member (send to random member) or use_mcast_xmit_req (send to everyone). Responses can also be sent + * to everyone instead of the requester by setting use_mcast_xmit to true. + * + * @author Bela Ban + * @version $Id: NAKACK.java,v 1.1 2012/08/17 14:51:22 marcin Exp $ + */ +public class NAKACK extends Protocol implements Retransmitter.RetransmitCommand, NakReceiverWindow.Listener { + private long[] retransmit_timeouts={600, 1200, 2400, 4800}; // time(s) to wait before requesting retransmission + private boolean is_server=false; + private Address local_addr=null; + private final List
        members=new CopyOnWriteArrayList
        (); + private View view; + @GuardedBy("seqno_lock") + private long seqno=0; // current message sequence number (starts with 1) + private final Lock seqno_lock=new ReentrantLock(); + private int gc_lag=20; // number of msgs garbage collection lags behind + private Map locks; + + private static final long INITIAL_SEQNO=0; + + /** + * Retransmit messages using multicast rather than unicast. This has the advantage that, if many receivers lost a + * message, the sender only retransmits once. + */ + private boolean use_mcast_xmit=true; + + /** Use a multicast to request retransmission of missing messages. This may be costly as every member in the cluster + * will send a response + */ + private boolean use_mcast_xmit_req=false; + + /** + * Ask a random member for retransmission of a missing message. If set to true, discard_delivered_msgs will be + * set to false + */ + private boolean xmit_from_random_member=false; + + + /** The first value (in milliseconds) to use in the exponential backoff retransmission mechanism. Only enabled + * if the value is > 0 + */ + private long exponential_backoff=0; + + /** If enabled, we use statistics gathered from actual retransmission times to compute the new retransmission times */ + private boolean use_stats_for_retransmission=false; + + /** + * Messages that have been received in order are sent up the stack (= delivered to the application). Delivered + * messages are removed from NakReceiverWindow.xmit_table and moved to NakReceiverWindow.delivered_msgs, where + * they are later garbage collected (by STABLE). Since we do retransmits only from sent messages, never + * received or delivered messages, we can turn the moving to delivered_msgs off, so we don't keep the message + * around, and don't need to wait for garbage collection to remove them. + */ + private boolean discard_delivered_msgs=false; + + + /** Whether to emit a warning on reception of messages from members not in our view */ + private boolean log_discard_msgs=true; + + /** + * By default, we release the lock on the sender in up() after the up() method call passed up the stack returns. + * However, with eager_lock_release enabled (default), we release the lock as soon as the application calls + * Channel.down() within the receive() callback. This leads to issues as the one described in + * http://jira.jboss.com/jira/browse/JGRP-656. Note that ordering is still correct , but messages from self + * might get delivered concurrently. This can be turned off by setting eager_lock_release to false. + */ + private boolean eager_lock_release=true; + + /** If value is > 0, the retransmit buffer is bounded: only the max_xmit_buf_size latest messages are kept, + * older ones are discarded when the buffer size is exceeded. A value <= 0 means unbounded buffers + */ + private int max_xmit_buf_size=0; + + + /** Map to store sent and received messages (keyed by sender) */ + private final ConcurrentMap xmit_table=new ConcurrentHashMap(11); + + /** Map which keeps track of threads removing messages from NakReceiverWindows, so we don't wait while a thread + * is removing messages */ + // private final ConcurrentMap in_progress=new ConcurrentHashMap(); + + private boolean leaving=false; + private boolean started=false; + private TimeScheduler timer=null; + private static final String name="NAKACK"; + + private long xmit_reqs_received; + private long xmit_reqs_sent; + private long xmit_rsps_received; + private long xmit_rsps_sent; + private long missing_msgs_received; + + /** Captures stats on XMIT_REQS, XMIT_RSPS per sender */ + private Map sent=new ConcurrentHashMap(); + + /** Captures stats on XMIT_REQS, XMIT_RSPS per receiver */ + private Map received=new ConcurrentHashMap(); + + private int stats_list_size=20; + + /** BoundedList. Keeps track of the last stats_list_size XMIT requests */ + private BoundedList receive_history; + + /** BoundedList. Keeps track of the last stats_list_size missing messages received */ + private BoundedList send_history; + + /** Per-sender map of seqnos and timestamps, to keep track of avg times for retransmission of messages */ + private final ConcurrentMap> xmit_stats=new ConcurrentHashMap>(); + + private int xmit_history_max_size=50; + + /** Maintains a list of the last N retransmission times (duration it took to retransmit a message) for all members */ + private final ConcurrentMap> xmit_times_history=new ConcurrentHashMap>(); + + /** Maintains a smoothed average of the retransmission times per sender, these are the actual values that are used for + * new retransmission requests */ + private final Map smoothed_avg_xmit_times=new HashMap(); + + /** the weight with which we take the previous smoothed average into account, WEIGHT should be >0 and <= 1 */ + private static final double WEIGHT=0.9; + + private static final double INITIAL_SMOOTHED_AVG=30.0; + + + // private final ConcurrentMap loss_rates=new ConcurrentHashMap(); + + + + /** + * Maintains retransmission related data across a time. Only used if enable_xmit_time_stats is set to true. + * At program termination, accumulated data is dumped to a file named by the address of the member. Careful, + * don't enable this in production as the data in this hashmap are never reaped ! Really only meant for + * diagnostics ! + */ + private ConcurrentMap xmit_time_stats=null; + private long xmit_time_stats_start; + + /** Keeps track of OOB messages sent by myself, needed by {@link #handleMessage(org.jgroups.Message, NakAckHeader)} */ + private final Set oob_loopback_msgs=Collections.synchronizedSet(new HashSet()); + + private final Lock rebroadcast_lock=new ReentrantLock(); + + private final Condition rebroadcast_done=rebroadcast_lock.newCondition(); + + // set during processing of a rebroadcast event + private volatile boolean rebroadcasting=false; + + private final Lock rebroadcast_digest_lock=new ReentrantLock(); + @GuardedBy("rebroadcast_digest_lock") + private Digest rebroadcast_digest=null; + + private long max_rebroadcast_timeout=2000; + + private static final int NUM_REBROADCAST_MSGS=3; + + /** BoundedList, keeps the last 10 stability messages */ + protected final BoundedList stability_msgs=new BoundedList(10); + + protected final BoundedList merge_history=new BoundedList(10); + + /** When not finding a message on an XMIT request, include the last N stability messages in the error message */ + protected boolean print_stability_history_on_failed_xmit=false; + + + + /** Regular messages which have been added, but not removed */ + private final AtomicInteger undelivered_msgs=new AtomicInteger(0); + + + + public NAKACK() { + } + + + public String getName() { + return name; + } + + public long getXmitRequestsReceived() {return xmit_reqs_received;} + public long getXmitRequestsSent() {return xmit_reqs_sent;} + public long getXmitResponsesReceived() {return xmit_rsps_received;} + public long getXmitResponsesSent() {return xmit_rsps_sent;} + public long getMissingMessagesReceived() {return missing_msgs_received;} + + public int getPendingRetransmissionRequests() { + int num=0; + for(NakReceiverWindow win: xmit_table.values()) { + num+=win.getPendingXmits(); + } + return num; + } + + public int getXmitTableSize() { + int num=0; + for(NakReceiverWindow win: xmit_table.values()) { + num+=win.size(); + } + return num; + } + + public int getReceivedTableSize() { + return getPendingRetransmissionRequests(); + } + + public void resetStats() { + xmit_reqs_received=xmit_reqs_sent=xmit_rsps_received=xmit_rsps_sent=missing_msgs_received=0; + sent.clear(); + received.clear(); + if(receive_history !=null) + receive_history.clear(); + if(send_history != null) + send_history.clear(); + stability_msgs.clear(); + merge_history.clear(); + } + + public void init() throws Exception { + if(stats) { + send_history=new BoundedList(stats_list_size); + receive_history=new BoundedList(stats_list_size); + } + } + + + public int getGcLag() { + return gc_lag; + } + + public void setGcLag(int gc_lag) { + this.gc_lag=gc_lag; + } + + public boolean isUseMcastXmit() { + return use_mcast_xmit; + } + + public void setUseMcastXmit(boolean use_mcast_xmit) { + this.use_mcast_xmit=use_mcast_xmit; + } + + public boolean isXmitFromRandomMember() { + return xmit_from_random_member; + } + + public void setXmitFromRandomMember(boolean xmit_from_random_member) { + this.xmit_from_random_member=xmit_from_random_member; + } + + public boolean isDiscardDeliveredMsgs() { + return discard_delivered_msgs; + } + + public void setDiscardDeliveredMsgs(boolean discard_delivered_msgs) { + boolean old=this.discard_delivered_msgs; + this.discard_delivered_msgs=discard_delivered_msgs; + if(old != this.discard_delivered_msgs) { + for(NakReceiverWindow win: xmit_table.values()) { + win.setDiscardDeliveredMessages(this.discard_delivered_msgs); + } + } + } + + public int getMaxXmitBufSize() { + return max_xmit_buf_size; + } + + public void setMaxXmitBufSize(int max_xmit_buf_size) { + this.max_xmit_buf_size=max_xmit_buf_size; + } + + /** + * + * @return + * @deprecated removed in 2.6 + */ + public long getMaxXmitSize() { + return -1; + } + + /** + * + * @param max_xmit_size + * @deprecated removed in 2.6 + */ + public void setMaxXmitSize(long max_xmit_size) { + } + + public boolean isLogDiscardMsgs() { + return log_discard_msgs; + } + + public void setLogDiscardMsgs(boolean log_discard_msgs) { + this.log_discard_msgs=log_discard_msgs; + } + + public boolean setProperties(Properties props) { + String str; + long[] tmp; + + super.setProperties(props); + str=props.getProperty("retransmit_timeout"); + if(str != null) { + tmp=Util.parseCommaDelimitedLongs(str); + props.remove("retransmit_timeout"); + if(tmp != null && tmp.length > 0) { + retransmit_timeouts=tmp; + } + } + + str=props.getProperty("gc_lag"); + if(str != null) { + gc_lag=Integer.parseInt(str); + if(gc_lag < 0) { + log.error("gc_lag cannot be negative, setting it to 0"); + } + props.remove("gc_lag"); + } + + str=props.getProperty("max_xmit_size"); + if(str != null) { + if(log.isWarnEnabled()) + log.warn("max_xmit_size was deprecated in 2.6 and will be ignored"); + props.remove("max_xmit_size"); + } + + str=props.getProperty("use_mcast_xmit"); + if(str != null) { + use_mcast_xmit=Boolean.valueOf(str).booleanValue(); + props.remove("use_mcast_xmit"); + } + + str=props.getProperty("use_mcast_xmit_req"); + if(str != null) { + use_mcast_xmit_req=Boolean.valueOf(str).booleanValue(); + props.remove("use_mcast_xmit_req"); + } + + str=props.getProperty("exponential_backoff"); + if(str != null) { + exponential_backoff=Long.parseLong(str); + props.remove("exponential_backoff"); + } + + str=props.getProperty("use_stats_for_retransmission"); + if(str != null) { + use_stats_for_retransmission=Boolean.valueOf(str); + props.remove("use_stats_for_retransmission"); + } + + str=props.getProperty("discard_delivered_msgs"); + if(str != null) { + discard_delivered_msgs=Boolean.valueOf(str); + props.remove("discard_delivered_msgs"); + } + + str=props.getProperty("xmit_from_random_member"); + if(str != null) { + xmit_from_random_member=Boolean.valueOf(str); + props.remove("xmit_from_random_member"); + } + + str=props.getProperty("max_xmit_buf_size"); + if(str != null) { + max_xmit_buf_size=Integer.parseInt(str); + props.remove("max_xmit_buf_size"); + } + + str=props.getProperty("stats_list_size"); + if(str != null) { + stats_list_size=Integer.parseInt(str); + props.remove("stats_list_size"); + } + + str=props.getProperty("xmit_history_max_size"); + if(str != null) { + xmit_history_max_size=Integer.parseInt(str); + props.remove("xmit_history_max_size"); + } + + str=props.getProperty("enable_xmit_time_stats"); + if(str != null) { + boolean enable_xmit_time_stats=Boolean.valueOf(str); + props.remove("enable_xmit_time_stats"); + if(enable_xmit_time_stats) { + if(log.isWarnEnabled()) + log.warn("enable_xmit_time_stats is experimental, and may be removed in any release"); + xmit_time_stats=new ConcurrentHashMap(); + xmit_time_stats_start=System.currentTimeMillis(); + } + } + + str=props.getProperty("max_rebroadcast_timeout"); + if(str != null) { + max_rebroadcast_timeout=Long.parseLong(str); + props.remove("max_rebroadcast_timeout"); + } + + str=props.getProperty("eager_lock_release"); + if(str != null) { + eager_lock_release=Boolean.valueOf(str).booleanValue(); + props.remove("eager_lock_release"); + } + + if(xmit_from_random_member) { + if(discard_delivered_msgs) { + discard_delivered_msgs=false; + log.warn("xmit_from_random_member set to true: changed discard_delivered_msgs to false"); + } + } + + str=props.getProperty("print_stability_history_on_failed_xmit"); + if(str != null) { + print_stability_history_on_failed_xmit=Boolean.valueOf(str).booleanValue(); + props.remove("print_stability_history_on_failed_xmit"); + } + + if(!props.isEmpty()) { + log.error("these properties are not recognized: " + props); + return false; + } + return true; + } + + public Map dumpStats() { + Map retval=super.dumpStats(); + if(retval == null) + retval=new HashMap(); + + retval.put("xmit_reqs_received", new Long(xmit_reqs_received)); + retval.put("xmit_reqs_sent", new Long(xmit_reqs_sent)); + retval.put("xmit_rsps_received", new Long(xmit_rsps_received)); + retval.put("xmit_rsps_sent", new Long(xmit_rsps_sent)); + retval.put("missing_msgs_received", new Long(missing_msgs_received)); + retval.put("msgs", printMessages()); + return retval; + } + + public String printStats() { + Map.Entry entry; + Object key, val; + StringBuilder sb=new StringBuilder(); + sb.append("sent:\n"); + for(Iterator it=sent.entrySet().iterator(); it.hasNext();) { + entry=(Map.Entry)it.next(); + key=entry.getKey(); + if(key == null) key=""; + val=entry.getValue(); + sb.append(key).append(": ").append(val).append("\n"); + } + sb.append("\nreceived:\n"); + for(Iterator it=received.entrySet().iterator(); it.hasNext();) { + entry=(Map.Entry)it.next(); + key=entry.getKey(); + val=entry.getValue(); + sb.append(key).append(": ").append(val).append("\n"); + } + + sb.append("\nXMIT_REQS sent:\n"); + for(XmitRequest tmp: send_history) { + sb.append(tmp).append("\n"); + } + + sb.append("\nMissing messages received\n"); + for(MissingMessage missing: receive_history) { + sb.append(missing).append("\n"); + } + + sb.append("\nStability messages received\n"); + sb.append(printStabilityMessages()).append("\n"); + + return sb.toString(); + } + + public String printStabilityMessages() { + StringBuilder sb=new StringBuilder(); + sb.append(Util.printListWithDelimiter(stability_msgs, "\n")); + return sb.toString(); + } + + public String printStabilityHistory() { + StringBuilder sb=new StringBuilder(); + int i=1; + for(Digest digest: stability_msgs) { + sb.append(i++).append(": ").append(digest).append("\n"); + } + return sb.toString(); + } + + public String printMergeHistory() { + StringBuilder sb=new StringBuilder(); + for(String tmp: merge_history) + sb.append(tmp).append("\n"); + return sb.toString(); + } + + public String printLossRates() { + StringBuilder sb=new StringBuilder(); + NakReceiverWindow win; + for(Map.Entry entry: xmit_table.entrySet()) { + win=entry.getValue(); + sb.append(entry.getKey()).append(": ").append(win.printLossRate()).append("\n"); + } + return sb.toString(); + } + + public double getAverageLossRate() { + double retval=0.0; + int count=0; + if(xmit_table.isEmpty()) + return 0.0; + for(NakReceiverWindow win: xmit_table.values()) { + retval+=win.getLossRate(); + count++; + } + return retval / (double)count; + } + + public double getAverageSmoothedLossRate() { + double retval=0.0; + int count=0; + if(xmit_table.isEmpty()) + return 0.0; + for(NakReceiverWindow win: xmit_table.values()) { + retval+=win.getSmoothedLossRate(); + count++; + } + return retval / (double)count; + } + + + public Vector providedUpServices() { + Vector retval=new Vector(5); + retval.addElement(new Integer(Event.GET_DIGEST)); + retval.addElement(new Integer(Event.SET_DIGEST)); + retval.addElement(new Integer(Event.MERGE_DIGEST)); + return retval; + } + + + public void start() throws Exception { + timer=getTransport().getTimer(); + if(timer == null) + throw new Exception("timer is null"); + locks=stack.getLocks(); + started=true; + + if(xmit_time_stats != null) { + Runtime.getRuntime().addShutdownHook(new Thread() { + public void run() { + String filename="xmit-stats-" + local_addr + ".log"; + System.out.println("-- dumping runtime xmit stats to " + filename); + try { + dumpXmitStats(filename); + } + catch(IOException e) { + e.printStackTrace(); + } + } + }); + } + } + + + public void stop() { + started=false; + reset(); // clears sent_msgs and destroys all NakReceiverWindows + oob_loopback_msgs.clear(); + } + + + /** + * Callback. Called by superclass when event may be handled.

        Do not use down_prot.down() in this + * method as the event is passed down by default by the superclass after this method returns ! + */ + public Object down(Event evt) { + switch(evt.getType()) { + + case Event.MSG: + Message msg=(Message)evt.getArg(); + Address dest=msg.getDest(); + if(dest != null && !dest.isMulticastAddress()) { + break; // unicast address: not null and not mcast, pass down unchanged + } + send(evt, msg); + return null; // don't pass down the stack + + case Event.STABLE: // generated by STABLE layer. Delete stable messages passed in arg + stable((Digest)evt.getArg()); + return null; // do not pass down further (Bela Aug 7 2001) + + case Event.GET_DIGEST: + return getDigest(); + + case Event.SET_DIGEST: + setDigest((Digest)evt.getArg()); + return null; + + case Event.MERGE_DIGEST: + mergeDigest((Digest)evt.getArg()); + return null; + + case Event.TMP_VIEW: + View tmp_view=(View)evt.getArg(); + Vector

        mbrs=tmp_view.getMembers(); + members.clear(); + members.addAll(mbrs); + // adjustReceivers(false); + break; + + case Event.VIEW_CHANGE: + tmp_view=(View)evt.getArg(); + mbrs=tmp_view.getMembers(); + members.clear(); + members.addAll(mbrs); + adjustReceivers(members); + is_server=true; // check vids from now on + + Set
        tmp=new LinkedHashSet
        (members); + tmp.add(null); // for null destination (= mcast) + sent.keySet().retainAll(tmp); + received.keySet().retainAll(tmp); + view=tmp_view; + xmit_stats.keySet().retainAll(tmp); + // in_progress.keySet().retainAll(mbrs); // remove elements which are not in the membership + break; + + case Event.BECOME_SERVER: + is_server=true; + break; + + case Event.DISCONNECT: + leaving=true; + reset(); + break; + + case Event.REBROADCAST: + rebroadcasting=true; + rebroadcast_digest=(Digest)evt.getArg(); + try { + rebroadcastMessages(); + } + finally { + rebroadcasting=false; + rebroadcast_digest_lock.lock(); + try { + rebroadcast_digest=null; + } + finally { + rebroadcast_digest_lock.unlock(); + } + } + return null; + } + + return down_prot.down(evt); + } + + + + + /** + * Callback. Called by superclass when event may be handled.

        Do not use PassUp in this + * method as the event is passed up by default by the superclass after this method returns ! + */ + public Object up(Event evt) { + switch(evt.getType()) { + + case Event.MSG: + Message msg=(Message)evt.getArg(); + NakAckHeader hdr=(NakAckHeader)msg.getHeader(name); + if(hdr == null) + break; // pass up (e.g. unicast msg) + + // discard messages while not yet server (i.e., until JOIN has returned) + if(!is_server) { + if(log.isTraceEnabled()) + log.trace("message was discarded (not yet server)"); + return null; + } + + // Changed by bela Jan 29 2003: we must not remove the header, otherwise + // further xmit requests will fail ! + //hdr=(NakAckHeader)msg.removeHeader(getName()); + + switch(hdr.type) { + + case NakAckHeader.MSG: + handleMessage(msg, hdr); + return null; // transmitter passes message up for us ! + + case NakAckHeader.XMIT_REQ: + if(hdr.range == null) { + if(log.isErrorEnabled()) { + log.error("XMIT_REQ: range of xmit msg is null; discarding request from " + msg.getSrc()); + } + return null; + } + handleXmitReq(msg.getSrc(), hdr.range.low, hdr.range.high, hdr.sender); + return null; + + case NakAckHeader.XMIT_RSP: + if(log.isTraceEnabled()) + log.trace("received missing message " + msg.getSrc() + ":" + hdr.seqno); + handleXmitRsp(msg); + return null; + + default: + if(log.isErrorEnabled()) { + log.error("NakAck header type " + hdr.type + " not known !"); + } + return null; + } + + case Event.STABLE: // generated by STABLE layer. Delete stable messages passed in arg + stable((Digest)evt.getArg()); + return null; // do not pass up further (Bela Aug 7 2001) + + case Event.SET_LOCAL_ADDRESS: + local_addr=(Address)evt.getArg(); + break; + + case Event.SUSPECT: + // release the promise if rebroadcasting is in progress... otherwise we wait forever. there will be a new + // flush round anyway + if(rebroadcasting) { + cancelRebroadcasting(); + } + break; + } + return up_prot.up(evt); + } + + + + + + + /* --------------------------------- Private Methods --------------------------------------- */ + + /** + * Adds the message to the sent_msgs table and then passes it down the stack. Change Bela Ban May 26 2002: we don't + * store a copy of the message, but a reference ! This saves us a lot of memory. However, this also means that a + * message should not be changed after storing it in the sent-table ! See protocols/DESIGN for details. + * Made seqno increment and adding to sent_msgs atomic, e.g. seqno won't get incremented if adding to + * sent_msgs fails e.g. due to an OOM (see http://jira.jboss.com/jira/browse/JGRP-179). bela Jan 13 2006 + */ + private void send(Event evt, Message msg) { + if(msg == null) + throw new NullPointerException("msg is null; event is " + evt); + + if(!started) { + if(log.isTraceEnabled()) + log.trace("[" + local_addr + "] discarded message as start() has not been called, message: " + msg); + return; + } + + long msg_id; + NakReceiverWindow win=xmit_table.get(local_addr); + msg.setSrc(local_addr); // this needs to be done so we can check whether the message sender is the local_addr + + seqno_lock.lock(); + try { + try { // incrementing seqno and adding the msg to sent_msgs needs to be atomic + msg_id=seqno +1; + msg.putHeader(name, new NakAckHeader(NakAckHeader.MSG, msg_id)); + win.add(msg_id, msg); + seqno=msg_id; + } + catch(Throwable t) { + throw new RuntimeException("failure adding msg " + msg + " to the retransmit table for " + local_addr, t); + } + } + finally { + seqno_lock.unlock(); + } + + try { // moved down_prot.down() out of synchronized clause (bela Sept 7 2006) http://jira.jboss.com/jira/browse/JGRP-300 + if(msg.isFlagSet(Message.OOB)) + oob_loopback_msgs.add(msg_id); + if(log.isTraceEnabled()) + log.trace("sending " + local_addr + "#" + msg_id); + down_prot.down(evt); // if this fails, since msg is in sent_msgs, it can be retransmitted + } + catch(Throwable t) { // eat the exception, don't pass it up the stack + if(log.isWarnEnabled()) { + log.warn("failure passing message down", t); + } + } + } + + + + /** + * Finds the corresponding NakReceiverWindow and adds the message to it (according to seqno). Then removes as many + * messages as possible from the NRW and passes them up the stack. Discards messages from non-members. + */ + private void handleMessage(Message msg, NakAckHeader hdr) { + Address sender=msg.getSrc(); + if(sender == null) { + if(log.isErrorEnabled()) + log.error("sender of message is null"); + return; + } + + if(log.isTraceEnabled()) + log.trace(new StringBuilder().append('[').append(local_addr).append(": received ").append(sender).append('#').append(hdr.seqno)); + + NakReceiverWindow win=xmit_table.get(sender); + if(win == null) { // discard message if there is no entry for sender + if(leaving) + return; + if(log.isWarnEnabled() && log_discard_msgs) + log.warn(local_addr + "] discarded message from non-member " + sender + ", my view is " + view); + return; + } + + boolean loopback=local_addr.equals(sender); + boolean added=loopback || win.add(hdr.seqno, msg); + boolean regular_msg_added=added && !msg.isFlagSet(Message.OOB); + + // message is passed up if OOB. Later, when remove() is called, we discard it. This affects ordering ! + // http://jira.jboss.com/jira/browse/JGRP-379 + if(added && msg.isFlagSet(Message.OOB)) { + if(!loopback || oob_loopback_msgs.remove(hdr.seqno)) { + up_prot.up(new Event(Event.MSG, msg)); + win.removeOOBMessage(); + if(!(win.hasMessagesToRemove() && undelivered_msgs.get() > 0)) + return; + } + } + + // Prevents concurrent passing up of messages by different threads (http://jira.jboss.com/jira/browse/JGRP-198); + // this is all the more important once we have a threadless stack (http://jira.jboss.com/jira/browse/JGRP-181), + // where lots of threads can come up to this point concurrently, but only 1 is allowed to pass at a time + // We *can* deliver messages from *different* senders concurrently, e.g. reception of P1, Q1, P2, Q2 can result in + // delivery of P1, Q1, Q2, P2: FIFO (implemented by NAKACK) says messages need to be delivered in the + // order in which they were sent by the sender + Message msg_to_deliver; + short removed_regular_msgs=0; + ReentrantLock lock=win.getLock(); + lock.lock(); + try { + if(eager_lock_release) + locks.put(Thread.currentThread(), lock); + + while((msg_to_deliver=win.remove()) != null) { + + // discard OOB msg as it has already been delivered (http://jira.jboss.com/jira/browse/JGRP-379) + if(msg_to_deliver.isFlagSet(Message.OOB)) { + continue; + } + removed_regular_msgs++; + + // Changed by bela Jan 29 2003: not needed (see above) + //msg_to_deliver.removeHeader(getName()); + up_prot.up(new Event(Event.MSG, msg_to_deliver)); + } + } + finally { + if(eager_lock_release) + locks.remove(Thread.currentThread()); + if(lock.isHeldByCurrentThread()) + lock.unlock(); + // We keep track of regular messages that we added, but couldn't remove (because of ordering). + // When we have such messages pending, then even OOB threads will remove and process them + // http://jira.jboss.com/jira/browse/JGRP-781 + if(regular_msg_added && removed_regular_msgs == 0) { + undelivered_msgs.incrementAndGet(); + } + if(removed_regular_msgs > 0) { // regardless of whether a message was added or not ! + int num_msgs_added=regular_msg_added? 1 : 0; + undelivered_msgs.addAndGet(-(removed_regular_msgs -num_msgs_added)); + } + } + } + + + + /** + * Retransmits messsages first_seqno to last_seqno from original_sender from xmit_table to xmit_requester, + * called when XMIT_REQ is received. + * @param xmit_requester The sender of the XMIT_REQ, we have to send the requested copy of the message to this address + * @param first_seqno The first sequence number to be retransmitted (<= last_seqno) + * @param last_seqno The last sequence number to be retransmitted (>= first_seqno) + * @param original_sender The member who originally sent the messsage. Guaranteed to be non-null + */ + private void handleXmitReq(Address xmit_requester, long first_seqno, long last_seqno, Address original_sender) { + Message msg; + + if(log.isTraceEnabled()) { + StringBuilder sb=new StringBuilder(); + sb.append(local_addr).append(": received xmit request from ").append(xmit_requester).append(" for "); + sb.append(original_sender).append(" [").append(first_seqno).append(" - ").append(last_seqno).append("]"); + log.trace(sb.toString()); + } + + if(first_seqno > last_seqno) { + if(log.isErrorEnabled()) + log.error("first_seqno (" + first_seqno + ") > last_seqno (" + last_seqno + "): not able to retransmit"); + return; + } + + if(stats) { + xmit_reqs_received+=last_seqno - first_seqno +1; + updateStats(received, xmit_requester, 1, 0, 0); + } + + if(xmit_time_stats != null) { + long key=(System.currentTimeMillis() - xmit_time_stats_start) / 1000; + XmitTimeStat stat=xmit_time_stats.get(key); + if(stat == null) { + stat=new XmitTimeStat(); + XmitTimeStat stat2=xmit_time_stats.putIfAbsent(key, stat); + if(stat2 != null) + stat=stat2; + } + stat.xmit_reqs_received.addAndGet((int)(last_seqno - first_seqno +1)); + stat.xmit_rsps_sent.addAndGet((int)(last_seqno - first_seqno +1)); + } + + NakReceiverWindow win=xmit_table.get(original_sender); + if(win == null) { + if(log.isErrorEnabled()) { + StringBuilder sb=new StringBuilder(); + sb.append("(requester=").append(xmit_requester).append(", local_addr=").append(this.local_addr); + sb.append(") ").append(original_sender).append(" not found in retransmission table:\n").append(printMessages()); + if(print_stability_history_on_failed_xmit) { + sb.append(" (stability history:\n").append(printStabilityHistory()); + } + log.error(sb); + } + return; + } + for(long i=first_seqno; i <= last_seqno; i++) { + msg=win.get(i); + if(msg == null || msg == NakReceiverWindow.NULL_MSG) { + if(log.isWarnEnabled() && !local_addr.equals(xmit_requester)) { + StringBuilder sb=new StringBuilder(); + sb.append("(requester=").append(xmit_requester).append(", local_addr=").append(this.local_addr); + sb.append(") message ").append(original_sender).append("::").append(i); + sb.append(" not found in retransmission table of ").append(original_sender).append(":\n").append(win); + if(print_stability_history_on_failed_xmit) { + sb.append(" (stability history:\n").append(printStabilityHistory()); + } + log.warn(sb); + } + continue; + } + sendXmitRsp(xmit_requester, msg, i); + } + } + + + + private void cancelRebroadcasting() { + rebroadcast_lock.lock(); + try { + rebroadcasting=false; + rebroadcast_done.signalAll(); + } + finally { + rebroadcast_lock.unlock(); + } + } + + + private static void updateStats(Map map, + Address key, + int req, + int rsp, + int missing) { + if(key != null) { + StatsEntry entry=map.get(key); + if(entry == null) { + entry=new StatsEntry(); + map.put(key, entry); + } + entry.xmit_reqs+=req; + entry.xmit_rsps+=rsp; + entry.missing_msgs_rcvd+=missing; + } + } + + + + /** + * Sends a message msg to the requester. We have to wrap the original message into a retransmit message, as we need + * to preserve the original message's properties, such as src, headers etc. + * @param dest + * @param msg + * @param seqno + */ + private void sendXmitRsp(Address dest, Message msg, long seqno) { + Buffer buf; + if(msg == null) { + if(log.isErrorEnabled()) + log.error("message is null, cannot send retransmission"); + return; + } + if(use_mcast_xmit) + dest=null; + + if(stats) { + xmit_rsps_sent++; + updateStats(sent, dest, 0, 1, 0); + } + + if(msg.getSrc() == null) + msg.setSrc(local_addr); + try { + buf=Util.messageToByteBuffer(msg); + Message xmit_msg=new Message(dest, null, buf.getBuf(), buf.getOffset(), buf.getLength()); + // changed Bela Jan 4 2007: we should not use OOB for retransmitted messages, otherwise we tax the OOB thread pool + // too much + // msg.setFlag(Message.OOB); + xmit_msg.putHeader(name, new NakAckHeader(NakAckHeader.XMIT_RSP, seqno)); + down_prot.down(new Event(Event.MSG, xmit_msg)); + } + catch(IOException ex) { + log.error("failed marshalling xmit list", ex); + } + } + + + private void handleXmitRsp(Message msg) { + if(msg == null) { + if(log.isWarnEnabled()) + log.warn("message is null"); + return; + } + + try { + Message wrapped_msg=Util.byteBufferToMessage(msg.getRawBuffer(), msg.getOffset(), msg.getLength()); + if(xmit_time_stats != null) { + long key=(System.currentTimeMillis() - xmit_time_stats_start) / 1000; + XmitTimeStat stat=xmit_time_stats.get(key); + if(stat == null) { + stat=new XmitTimeStat(); + XmitTimeStat stat2=xmit_time_stats.putIfAbsent(key, stat); + if(stat2 != null) + stat=stat2; + } + stat.xmit_rsps_received.incrementAndGet(); + } + + if(stats) { + xmit_rsps_received++; + updateStats(received, msg.getSrc(), 0, 1, 0); + } + + up(new Event(Event.MSG, wrapped_msg)); + + if(rebroadcasting) { + Digest tmp=getDigest(); + boolean cancel_rebroadcasting; + rebroadcast_digest_lock.lock(); + try { + cancel_rebroadcasting=tmp.isGreaterThanOrEqual(rebroadcast_digest); + } + finally { + rebroadcast_digest_lock.unlock(); + } + if(cancel_rebroadcasting) { + cancelRebroadcasting(); + } + } + } + catch(Exception ex) { + if(log.isErrorEnabled()) { + log.error("failed reading retransmitted message", ex); + } + } + } + + + /** + * Takes the argument highest_seqnos and compares it to the current digest. If the current digest has fewer messages, + * then send retransmit messages for the missing messages. Return when all missing messages have been received. If + * we're waiting for a missing message from P, and P crashes while waiting, we need to exclude P from the wait set. + */ + private void rebroadcastMessages() { + Digest my_digest; + Map their_digest; + Address sender; + Digest.Entry their_entry, my_entry; + long their_high, my_high; + long sleep=max_rebroadcast_timeout / NUM_REBROADCAST_MSGS; + long wait_time=max_rebroadcast_timeout, start=System.currentTimeMillis(); + + while(wait_time > 0) { + rebroadcast_digest_lock.lock(); + try { + if(rebroadcast_digest == null) + break; + their_digest=rebroadcast_digest.getSenders(); + } + finally { + rebroadcast_digest_lock.unlock(); + } + my_digest=getDigest(); + + boolean xmitted=false; + for(Map.Entry entry: their_digest.entrySet()) { + sender=entry.getKey(); + their_entry=entry.getValue(); + my_entry=my_digest.get(sender); + if(my_entry == null) + continue; + their_high=their_entry.getHighest(); + my_high=my_entry.getHighest(); + if(their_high > my_high) { + if(log.isTraceEnabled()) + log.trace("sending XMIT request to " + sender + " for messages " + my_high + " - " + their_high); + retransmit(my_high, their_high, sender, true); // use multicast to send retransmit request + xmitted=true; + } + } + if(!xmitted) + return; // we're done; no retransmissions are needed anymore. our digest is >= rebroadcast_digest + + rebroadcast_lock.lock(); + try { + try { + my_digest=getDigest(); + rebroadcast_digest_lock.lock(); + try { + if(!rebroadcasting || my_digest.isGreaterThanOrEqual(rebroadcast_digest)) + return; + } + finally { + rebroadcast_digest_lock.unlock(); + } + rebroadcast_done.await(sleep, TimeUnit.MILLISECONDS); + wait_time-=(System.currentTimeMillis() - start); + } + catch(InterruptedException e) { + } + } + finally { + rebroadcast_lock.unlock(); + } + } + } + + + /** + * Remove old members from NakReceiverWindows and add new members (starting seqno=0). Essentially removes all + * entries from xmit_table that are not in members. This method is not called concurrently + * multiple times + */ + private void adjustReceivers(List

        new_members) { + NakReceiverWindow win; + + // 1. Remove all senders in xmit_table that are not members anymore + for(Iterator
        it=xmit_table.keySet().iterator(); it.hasNext();) { + Address sender=it.next(); + if(!new_members.contains(sender)) { + if(local_addr != null && local_addr.equals(sender)) { + if(log.isErrorEnabled()) + log.error("will not remove myself (" + sender + ") from xmit_table, received incorrect new membership of " + new_members); + continue; + } + win=xmit_table.get(sender); + win.reset(); + if(log.isDebugEnabled()) { + log.debug("removing " + sender + " from xmit_table (not member anymore)"); + } + it.remove(); + } + } + + // 2. Add newly joined members to xmit_table (starting seqno=0) + for(Address sender: new_members) { + if(!xmit_table.containsKey(sender)) { + win=createNakReceiverWindow(sender, INITIAL_SEQNO, 0); + xmit_table.put(sender, win); + } + } + } + + + /** + * Returns a message digest: for each member P the lowest, highest delivered and highest received seqno is added + */ + private Digest getDigest() { + Digest.Entry entry; + + Map map=new HashMap(members.size()); + for(Address sender: members) { + entry=getEntry(sender); + if(entry == null) { + if(log.isErrorEnabled()) { + log.error("range is null"); + } + continue; + } + map.put(sender, entry); + } + return new Digest(map); + } + + + + /** + * Creates a NakReceiverWindow for each sender in the digest according to the sender's seqno. If NRW already exists, + * reset it. + */ + private void setDigest(Digest digest) { + if(digest == null) { + if(log.isErrorEnabled()) { + log.error("digest or digest.senders is null"); + } + return; + } + + if(local_addr != null && digest.contains(local_addr)) { + clear(); + } + else { + // remove all but local_addr (if not null) + for(Iterator
        it=xmit_table.keySet().iterator(); it.hasNext();) { + Address key=it.next(); + if(local_addr != null && local_addr.equals(key)) { + ; + } + else { + it.remove(); + } + } + } + + Address sender; + Digest.Entry val; + long initial_seqno; + NakReceiverWindow win; + + for(Map.Entry entry: digest.getSenders().entrySet()) { + sender=entry.getKey(); + val=entry.getValue(); + if(sender == null || val == null) { + if(log.isWarnEnabled()) { + log.warn("sender or value is null"); + } + continue; + } + initial_seqno=val.getHighestDeliveredSeqno(); + win=createNakReceiverWindow(sender, initial_seqno, val.getLow()); + xmit_table.put(sender, win); + } + if(!xmit_table.containsKey(local_addr)) { + if(log.isWarnEnabled()) { + log.warn("digest does not contain local address (local_addr=" + local_addr + ", digest=" + digest); + } + } + } + + + /** + * For all members of the digest, adjust the NakReceiverWindows in the xmit_table hashtable. If no entry + * exists, create one with the initial seqno set to the seqno of the member in the digest. If the member already + * exists, and is not the local address, replace it with the new entry (http://jira.jboss.com/jira/browse/JGRP-699) + */ + private void mergeDigest(Digest digest) { + if(digest == null) { + if(log.isErrorEnabled()) { + log.error("digest or digest.senders is null"); + } + return; + } + + StringBuilder sb=new StringBuilder(); + sb.append("existing digest: " + getDigest()).append("\nnew digest: " + digest); + + for(Map.Entry entry: digest.getSenders().entrySet()) { + Address sender=entry.getKey(); + Digest.Entry val=entry.getValue(); + if(sender == null || val == null) { + if(log.isWarnEnabled()) { + log.warn("sender or value is null"); + } + continue; + } + long highest_delivered_seqno=val.getHighestDeliveredSeqno(); + long low_seqno=val.getLow(); + + // changed Feb 2008 (bela): http://jira.jboss.com/jira/browse/JGRP-699: we replace all existing entries + // except for myself + NakReceiverWindow win=xmit_table.get(sender); + if(win != null) { + if(local_addr != null && local_addr.equals(sender)) { + continue; + } + else { + win.reset(); // stops retransmission + xmit_table.remove(sender); + } + } + win=createNakReceiverWindow(sender, highest_delivered_seqno, low_seqno); + xmit_table.put(sender, win); + } + sb.append("\n").append("resulting digest: " + getDigest()); + merge_history.add(sb.toString()); + if(log.isDebugEnabled()) + log.debug(sb); + + if(!xmit_table.containsKey(local_addr)) { + if(log.isWarnEnabled()) { + log.warn("digest does not contain local address (local_addr=" + local_addr + ", digest=" + digest); + } + } + } + + + private NakReceiverWindow createNakReceiverWindow(Address sender, long initial_seqno, long lowest_seqno) { + NakReceiverWindow win=new NakReceiverWindow(local_addr, sender, this, initial_seqno, lowest_seqno, timer); + + if(use_stats_for_retransmission) { + win.setRetransmitTimeouts(new ActualInterval(sender)); + } + else if(exponential_backoff > 0) { + win.setRetransmitTimeouts(new ExponentialInterval(exponential_backoff)); + } + else { + win.setRetransmitTimeouts(new StaticInterval(retransmit_timeouts)); + } + + win.setDiscardDeliveredMessages(discard_delivered_msgs); + win.setMaxXmitBufSize(this.max_xmit_buf_size); + if(stats) + win.setListener(this); + return win; + } + + + private void dumpXmitStats(String filename) throws IOException { + Writer out=new FileWriter(filename); + try { + TreeMap map=new TreeMap(xmit_time_stats); + StringBuilder sb; + XmitTimeStat stat; + out.write("time (secs) gaps-detected xmit-reqs-sent xmit-reqs-received xmit-rsps-sent xmit-rsps-received missing-msgs-received\n\n"); + for(Map.Entry entry: map.entrySet()) { + sb=new StringBuilder(); + stat=entry.getValue(); + sb.append(entry.getKey()).append(" "); + sb.append(stat.gaps_detected).append(" "); + sb.append(stat.xmit_reqs_sent).append(" "); + sb.append(stat.xmit_reqs_received).append(" "); + sb.append(stat.xmit_rsps_sent).append(" "); + sb.append(stat.xmit_rsps_received).append(" "); + sb.append(stat.missing_msgs_received).append("\n"); + out.write(sb.toString()); + } + } + finally { + out.close(); + } + } + + + + private Digest.Entry getEntry(Address sender) { + if(sender == null) { + if(log.isErrorEnabled()) { + log.error("sender is null"); + } + return null; + } + NakReceiverWindow win=xmit_table.get(sender); + if(win == null) { + if(log.isErrorEnabled()) { + log.error("sender " + sender + " not found in xmit_table"); + } + return null; + } + long low=win.getLowestSeen(), + highest_delivered=win.getHighestDelivered(), + highest_received=win.getHighestReceived(); + return new Digest.Entry(low, highest_delivered, highest_received); + } + + + + /** + * Garbage collect messages that have been seen by all members. Update sent_msgs: for the sender P in the digest + * which is equal to the local address, garbage collect all messages <= seqno at digest[P]. Update xmit_table: + * for each sender P in the digest and its highest seqno seen SEQ, garbage collect all delivered_msgs in the + * NakReceiverWindow corresponding to P which are <= seqno at digest[P]. + */ + private void stable(Digest digest) { + NakReceiverWindow recv_win; + long my_highest_rcvd; // highest seqno received in my digest for a sender P + long stability_highest_rcvd; // highest seqno received in the stability vector for a sender P + + if(members == null || local_addr == null || digest == null) { + if(log.isWarnEnabled()) + log.warn("members, local_addr or digest are null !"); + return; + } + + if(log.isTraceEnabled()) { + log.trace("received stable digest " + digest); + } + + stability_msgs.add(digest); + + Address sender; + Digest.Entry val; + long high_seqno_delivered, high_seqno_received; + + for(Map.Entry entry: digest.getSenders().entrySet()) { + sender=entry.getKey(); + if(sender == null) + continue; + val=entry.getValue(); + high_seqno_delivered=val.getHighestDeliveredSeqno(); + high_seqno_received=val.getHighestReceivedSeqno(); + + + // check whether the last seqno received for a sender P in the stability vector is > last seqno + // received for P in my digest. if yes, request retransmission (see "Last Message Dropped" topic + // in DESIGN) + recv_win=xmit_table.get(sender); + if(recv_win != null) { + my_highest_rcvd=recv_win.getHighestReceived(); + stability_highest_rcvd=high_seqno_received; + + if(stability_highest_rcvd >= 0 && stability_highest_rcvd > my_highest_rcvd) { + if(log.isTraceEnabled()) { + log.trace("my_highest_rcvd (" + my_highest_rcvd + ") < stability_highest_rcvd (" + + stability_highest_rcvd + "): requesting retransmission of " + + sender + '#' + stability_highest_rcvd); + } + retransmit(stability_highest_rcvd, stability_highest_rcvd, sender); + } + } + + high_seqno_delivered-=gc_lag; + if(high_seqno_delivered < 0) { + continue; + } + + if(log.isTraceEnabled()) + log.trace("deleting msgs <= " + high_seqno_delivered + " from " + sender); + + // delete *delivered* msgs that are stable + if(recv_win != null) { + recv_win.stable(high_seqno_delivered); // delete all messages with seqnos <= seqno + } + } + } + + + + /* ---------------------- Interface Retransmitter.RetransmitCommand ---------------------- */ + + + /** + * Implementation of Retransmitter.RetransmitCommand. Called by retransmission thread when gap is detected. + */ + public void retransmit(long first_seqno, long last_seqno, Address sender) { + retransmit(first_seqno, last_seqno, sender, false); + } + + + + protected void retransmit(long first_seqno, long last_seqno, Address sender, boolean multicast_xmit_request) { + NakAckHeader hdr; + Message retransmit_msg; + Address dest=sender; // to whom do we send the XMIT request ? + + if(multicast_xmit_request || this.use_mcast_xmit_req) { + dest=null; + } + else { + if(xmit_from_random_member && !local_addr.equals(sender)) { + Address random_member=(Address)Util.pickRandomElement(members); + if(random_member != null && !local_addr.equals(random_member)) { + dest=random_member; + if(log.isTraceEnabled()) + log.trace("picked random member " + dest + " to send XMIT request to"); + } + } + } + + hdr=new NakAckHeader(NakAckHeader.XMIT_REQ, first_seqno, last_seqno, sender); + retransmit_msg=new Message(dest, null, null); + retransmit_msg.setFlag(Message.OOB); + if(log.isTraceEnabled()) + log.trace(local_addr + ": sending XMIT_REQ ([" + first_seqno + ", " + last_seqno + "]) to " + dest); + retransmit_msg.putHeader(name, hdr); + + ConcurrentMap tmp=xmit_stats.get(sender); + if(tmp == null) { + tmp=new ConcurrentHashMap(); + ConcurrentMap tmp2=xmit_stats.putIfAbsent(sender, tmp); + if(tmp2 != null) + tmp=tmp2; + } + for(long seq=first_seqno; seq < last_seqno; seq++) { + tmp.putIfAbsent(seq, System.currentTimeMillis()); + } + + if(xmit_time_stats != null) { + long key=(System.currentTimeMillis() - xmit_time_stats_start) / 1000; + XmitTimeStat stat=xmit_time_stats.get(key); + if(stat == null) { + stat=new XmitTimeStat(); + XmitTimeStat stat2=xmit_time_stats.putIfAbsent(key, stat); + if(stat2 != null) + stat=stat2; + } + stat.xmit_reqs_sent.addAndGet((int)(last_seqno - first_seqno +1)); + } + + down_prot.down(new Event(Event.MSG, retransmit_msg)); + if(stats) { + xmit_reqs_sent+=last_seqno - first_seqno +1; + updateStats(sent, dest, 1, 0, 0); + XmitRequest req=new XmitRequest(sender, first_seqno, last_seqno, dest); + send_history.add(req); + } + } + /* ------------------- End of Interface Retransmitter.RetransmitCommand -------------------- */ + + + + /* ----------------------- Interface NakReceiverWindow.Listener ---------------------- */ + + public void missingMessageReceived(long seqno, Address original_sender) { + ConcurrentMap tmp=xmit_stats.get(original_sender); + if(tmp != null) { + Long timestamp=tmp.remove(seqno); + if(timestamp != null) { + long diff=System.currentTimeMillis() - timestamp; + BoundedList list=xmit_times_history.get(original_sender); + if(list == null) { + list=new BoundedList(xmit_history_max_size); + BoundedList list2=xmit_times_history.putIfAbsent(original_sender, list); + if(list2 != null) + list=list2; + } + list.add(diff); + + // compute the smoothed average for retransmission times for original_sender + synchronized(smoothed_avg_xmit_times) { + Double smoothed_avg=smoothed_avg_xmit_times.get(original_sender); + if(smoothed_avg == null) + smoothed_avg=INITIAL_SMOOTHED_AVG; + // the smoothed avg takes 90% of the previous value, 100% of the new value and averages them + // then, we add 10% to be on the safe side (an xmit value should rather err on the higher than lower side) + smoothed_avg=((smoothed_avg * WEIGHT) + diff) / 2; + smoothed_avg=smoothed_avg * (2 - WEIGHT); + smoothed_avg_xmit_times.put(original_sender, smoothed_avg); + } + } + } + + if(xmit_time_stats != null) { + long key=(System.currentTimeMillis() - xmit_time_stats_start) / 1000; + XmitTimeStat stat=xmit_time_stats.get(key); + if(stat == null) { + stat=new XmitTimeStat(); + XmitTimeStat stat2=xmit_time_stats.putIfAbsent(key, stat); + if(stat2 != null) + stat=stat2; + } + stat.missing_msgs_received.incrementAndGet(); + } + + if(stats) { + missing_msgs_received++; + updateStats(received, original_sender, 0, 0, 1); + MissingMessage missing=new MissingMessage(original_sender, seqno); + receive_history.add(missing); + } + } + + /** Called when a message gap is detected */ + public void messageGapDetected(long from, long to, Address src) { + if(xmit_time_stats != null) { + long key=(System.currentTimeMillis() - xmit_time_stats_start) / 1000; + XmitTimeStat stat=xmit_time_stats.get(key); + if(stat == null) { + stat=new XmitTimeStat(); + XmitTimeStat stat2=xmit_time_stats.putIfAbsent(key, stat); + if(stat2 != null) + stat=stat2; + } + stat.gaps_detected.addAndGet((int)(to - from +1)); + } + } + + /* ------------------- End of Interface NakReceiverWindow.Listener ------------------- */ + + private void clear() { + // changed April 21 2004 (bela): SourceForge bug# 938584. We cannot delete our own messages sent between + // a join() and a getState(). Otherwise retransmission requests from members who missed those msgs might + // fail. Not to worry though: those msgs will be cleared by STABLE (message garbage collection) + + // sent_msgs.clear(); + + for(NakReceiverWindow win: xmit_table.values()) { + win.reset(); + } + xmit_table.clear(); + undelivered_msgs.set(0); + } + + + private void reset() { + seqno_lock.lock(); + try { + seqno=0; + } + finally { + seqno_lock.unlock(); + } + + for(NakReceiverWindow win: xmit_table.values()) { + win.destroy(); + } + xmit_table.clear(); + undelivered_msgs.set(0); + } + + + public String printMessages() { + StringBuilder ret=new StringBuilder(); + Map.Entry entry; + Address addr; + Object w; + + for(Iterator> it=xmit_table.entrySet().iterator(); it.hasNext();) { + entry=it.next(); + addr=entry.getKey(); + w=entry.getValue(); + ret.append(addr).append(": ").append(w.toString()).append('\n'); + } + return ret.toString(); + } + + + public String printRetransmissionAvgs() { + StringBuilder sb=new StringBuilder(); + + for(Map.Entry> entry: xmit_times_history.entrySet()) { + Address sender=entry.getKey(); + BoundedList list=entry.getValue(); + long tmp=0; + int i=0; + for(Long val: list) { + tmp+=val; + i++; + } + double avg=i > 0? tmp / i: -1; + sb.append(sender).append(": ").append(avg).append("\n"); + } + return sb.toString(); + } + + public String printSmoothedRetransmissionAvgs() { + StringBuilder sb=new StringBuilder(); + for(Map.Entry entry: smoothed_avg_xmit_times.entrySet()) { + sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); + } + return sb.toString(); + } + + public String printRetransmissionTimes() { + StringBuilder sb=new StringBuilder(); + + for(Map.Entry> entry: xmit_times_history.entrySet()) { + Address sender=entry.getKey(); + BoundedList list=entry.getValue(); + sb.append(sender).append(": ").append(list).append("\n"); + } + return sb.toString(); + } + + public double getTotalAverageRetransmissionTime() { + long total=0; + int i=0; + + for(BoundedList list: xmit_times_history.values()) { + for(Long val: list) { + total+=val; + i++; + } + } + return i > 0? total / i: -1; + } + + public double getTotalAverageSmoothedRetransmissionTime() { + double total=0.0; + int cnt=0; + synchronized(smoothed_avg_xmit_times) { + for(Double val: smoothed_avg_xmit_times.values()) { + if(val != null) { + total+=val; + cnt++; + } + } + } + return cnt > 0? total / cnt : -1; + } + + /** Returns the smoothed average retransmission time for a given sender */ + public double getSmoothedAverageRetransmissionTime(Address sender) { + synchronized(smoothed_avg_xmit_times) { + Double retval=smoothed_avg_xmit_times.get(sender); + if(retval == null) { + retval=INITIAL_SMOOTHED_AVG; + smoothed_avg_xmit_times.put(sender, retval); + } + return retval; + } + } + + +// public static final class LossRate { +// private final Set received=new HashSet(); +// private final Set missing=new HashSet(); +// private double smoothed_loss_rate=0.0; +// +// public synchronized void addReceived(long seqno) { +// received.add(seqno); +// missing.remove(seqno); +// setSmoothedLossRate(); +// } +// +// public synchronized void addReceived(Long ... seqnos) { +// for(int i=0; i < seqnos.length; i++) { +// Long seqno=seqnos[i]; +// received.add(seqno); +// missing.remove(seqno); +// } +// setSmoothedLossRate(); +// } +// +// public synchronized void addMissing(long from, long to) { +// for(long i=from; i <= to; i++) { +// if(!received.contains(i)) +// missing.add(i); +// } +// setSmoothedLossRate(); +// } +// +// public synchronized double computeLossRate() { +// int num_missing=missing.size(); +// if(num_missing == 0) +// return 0.0; +// int num_received=received.size(); +// int total=num_missing + num_received; +// return num_missing / (double)total; +// } +// +// public synchronized double getSmoothedLossRate() { +// return smoothed_loss_rate; +// } +// +// public synchronized String toString() { +// StringBuilder sb=new StringBuilder(); +// int num_missing=missing.size(); +// int num_received=received.size(); +// int total=num_missing + num_received; +// sb.append("total=").append(total).append(" (received=").append(received.size()).append(", missing=") +// .append(missing.size()).append(", loss rate=").append(computeLossRate()) +// .append(", smoothed loss rate=").append(smoothed_loss_rate).append(")"); +// return sb.toString(); +// } +// +// /** Set the new smoothed_loss_rate value to 70% of the new value and 30% of the old value */ +// private void setSmoothedLossRate() { +// double new_loss_rate=computeLossRate(); +// if(smoothed_loss_rate == 0) { +// smoothed_loss_rate=new_loss_rate; +// } +// else { +// smoothed_loss_rate=smoothed_loss_rate * .3 + new_loss_rate * .7; +// } +// } +// } + + private static class XmitTimeStat { + final AtomicInteger gaps_detected=new AtomicInteger(0); + final AtomicInteger xmit_reqs_sent=new AtomicInteger(0); + final AtomicInteger xmit_reqs_received=new AtomicInteger(0); + final AtomicInteger xmit_rsps_sent=new AtomicInteger(0); + final AtomicInteger xmit_rsps_received=new AtomicInteger(0); + final AtomicInteger missing_msgs_received=new AtomicInteger(0); + } + + private class ActualInterval implements Interval { + private final Address sender; + + public ActualInterval(Address sender) { + this.sender=sender; + } + + public long next() { + return (long)getSmoothedAverageRetransmissionTime(sender); + } + + public Interval copy() { + return this; + } + } + + static class StatsEntry { + long xmit_reqs, xmit_rsps, missing_msgs_rcvd; + + public String toString() { + StringBuilder sb=new StringBuilder(); + sb.append(xmit_reqs).append(" xmit_reqs").append(", ").append(xmit_rsps).append(" xmit_rsps"); + sb.append(", ").append(missing_msgs_rcvd).append(" missing msgs"); + return sb.toString(); + } + } + + static class XmitRequest { + Address original_sender; // original sender of message + long low, high, timestamp=System.currentTimeMillis(); + Address xmit_dest; // destination to which XMIT_REQ is sent, usually the original sender + + XmitRequest(Address original_sender, long low, long high, Address xmit_dest) { + this.original_sender=original_sender; + this.xmit_dest=xmit_dest; + this.low=low; + this.high=high; + } + + public String toString() { + StringBuilder sb=new StringBuilder(); + sb.append(new Date(timestamp)).append(": ").append(original_sender).append(" #[").append(low); + sb.append("-").append(high).append("]"); + sb.append(" (XMIT_REQ sent to ").append(xmit_dest).append(")"); + return sb.toString(); + } + } + + static class MissingMessage { + Address original_sender; + long seq, timestamp=System.currentTimeMillis(); + + MissingMessage(Address original_sender, long seqno) { + this.original_sender=original_sender; + this.seq=seqno; + } + + public String toString() { + StringBuilder sb=new StringBuilder(); + sb.append(new Date(timestamp)).append(": ").append(original_sender).append(" #").append(seq); + return sb.toString(); + } + } + + /* ----------------------------- End of Private Methods ------------------------------------ */ + + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/NakAckHeader.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/NakAckHeader.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/NakAckHeader.java 17 Aug 2012 14:51:22 -0000 1.1 @@ -0,0 +1,155 @@ + +package org.jgroups.protocols.pbcast; + + +import org.jgroups.Address; +import org.jgroups.Global; +import org.jgroups.Header; +import org.jgroups.util.Range; +import org.jgroups.util.Streamable; +import org.jgroups.util.Util; + +import java.io.*; + + +/** + * @author Bela Ban + * @version $Id: NakAckHeader.java,v 1.1 2012/08/17 14:51:22 marcin Exp $ + */ +public class NakAckHeader extends Header implements Streamable { + public static final byte MSG=1; // regular msg + public static final byte XMIT_REQ=2; // retransmit request + public static final byte XMIT_RSP=3; // retransmit response (contains one or more messages) + + + byte type=0; + long seqno=-1; // seqno of regular message (MSG) + Range range=null; // range of msgs to be retransmitted (XMIT_REQ) or retransmitted (XMIT_RSP) + Address sender; // the original sender of the message (for XMIT_REQ) + private static final long serialVersionUID=-4305600151593420827L; + + + public NakAckHeader() { + } + + + /** + * Constructor for regular messages + */ + public NakAckHeader(byte type, long seqno) { + this.type=type; + this.seqno=seqno; + } + + /** + * Constructor for retransmit requests/responses (low and high define the range of msgs) + */ + public NakAckHeader(byte type, long low, long high) { + this.type=type; + range=new Range(low, high); + } + + + public NakAckHeader(byte type, long low, long high, Address sender) { + this(type, low, high); + this.sender=sender; + } + + + + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeByte(type); + out.writeLong(seqno); + if(range != null) { + out.writeBoolean(true); // wasn't here before, bad bug ! + range.writeExternal(out); + } + else + out.writeBoolean(false); + out.writeObject(sender); + } + + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + boolean read_range; + type=in.readByte(); + seqno=in.readLong(); + read_range=in.readBoolean(); + if(read_range) { + range=new Range(); + range.readExternal(in); + } + sender=(Address)in.readObject(); + } + + public void writeTo(DataOutputStream out) throws IOException { + out.writeByte(type); + out.writeLong(seqno); + Util.writeStreamable(range, out); + Util.writeAddress(sender, out); + } + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + type=in.readByte(); + seqno=in.readLong(); + range=(Range)Util.readStreamable(Range.class, in); + sender=Util.readAddress(in); + } + + public int size() { + // type (1 byte) + seqno (8 bytes) + int retval=Global.BYTE_SIZE; + retval+=Global.LONG_SIZE; + retval+=Global.BYTE_SIZE; // presence for range + if(range != null) + retval+=2 * Global.LONG_SIZE; // 2 times 8 bytes for seqno + retval+=Util.size(sender); + return retval; + } + + + public NakAckHeader copy() { + NakAckHeader ret=new NakAckHeader(type, seqno); + ret.range=range; + ret.sender=sender; + return ret; + } + + + public static String type2Str(byte t) { + switch(t) { + case MSG: + return "MSG"; + case XMIT_REQ: + return "XMIT_REQ"; + case XMIT_RSP: + return "XMIT_RSP"; + default: + return ""; + } + } + + + public String toString() { + StringBuilder ret=new StringBuilder(); + ret.append("[").append(type2Str(type)); + switch(type) { + case MSG: // seqno and sender + ret.append(", seqno=").append(seqno); + break; + case XMIT_REQ: // range and sender + if(range != null) + ret.append(", range=" + range); + break; + case XMIT_RSP: // seqno and sender + break; + } + + if(sender != null) ret.append(", sender=").append(sender); + ret.append(']'); + return ret.toString(); + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/ParticipantGmsImpl.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/ParticipantGmsImpl.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/ParticipantGmsImpl.java 17 Aug 2012 14:51:22 -0000 1.1 @@ -0,0 +1,213 @@ + +package org.jgroups.protocols.pbcast; + +import org.jgroups.*; +import org.jgroups.util.Promise; +import org.jgroups.util.Digest; + +import java.util.Vector; +import java.util.Iterator; +import java.util.Collection; +import java.util.LinkedHashSet; + + +/** + * @author Bela Ban + * @version $Id: ParticipantGmsImpl.java,v 1.1 2012/08/17 14:51:22 marcin Exp $ + */ +public class ParticipantGmsImpl extends GmsImpl { + private final Vector
        suspected_mbrs=new Vector
        (11); + private final Promise leave_promise=new Promise(); + + + public ParticipantGmsImpl(GMS g) { + super(g); + } + + + public void init() throws Exception { + super.init(); + suspected_mbrs.removeAllElements(); + leave_promise.reset(); + } + + public void join(Address mbr) { + wrongMethod("join"); + } + + public void joinWithStateTransfer(Address mbr) { + wrongMethod("join"); + } + + + /** + * Loop: determine coord. If coord is me --> handleLeave(). + * Else send handleLeave() to coord until success + */ + public void leave(Address mbr) { + Address coord; + int max_tries=3; + Boolean result; + + leave_promise.reset(); + + if(mbr.equals(gms.local_addr)) + leaving=true; + + while((coord=gms.determineCoordinator()) != null && max_tries-- > 0) { + if(gms.local_addr.equals(coord)) { // I'm the coordinator + gms.becomeCoordinator(); + // gms.getImpl().handleLeave(mbr, false); // regular leave + gms.getImpl().leave(mbr); // regular leave + return; + } + + if(log.isDebugEnabled()) log.debug("sending LEAVE request to " + coord + " (local_addr=" + gms.local_addr + ")"); + sendLeaveMessage(coord, mbr); + result=leave_promise.getResult(gms.leave_timeout); + if(result != null) + break; + } + gms.becomeClient(); + } + + + /** In case we get a different JOIN_RSP from a previous JOIN_REQ sent by us (as a client), we simply apply the + * new view if it is greater than ours + * + * @param join_rsp + */ + public void handleJoinResponse(JoinRsp join_rsp) { + View v=join_rsp.getView(); + ViewId tmp_vid=v != null? v.getVid() : null; + if(tmp_vid != null && gms.view_id != null && tmp_vid.compareTo(gms.view_id) > 0) { + gms.installView(v); + } + } + + public void handleLeaveResponse() { + leave_promise.setResult(true); // unblocks thread waiting in leave() + } + + + public void suspect(Address mbr) { + Collection suspected=new LinkedHashSet(1); + suspected.add(new Request(Request.SUSPECT,mbr,true,null)); + handleMembershipChange(suspected); + } + + + /** Removes previously suspected member from list of currently suspected members */ + public void unsuspect(Address mbr) { + if(mbr != null) + suspected_mbrs.remove(mbr); + } + + + public void handleMembershipChange(Collection requests) { + Collection
        suspectedMembers=new LinkedHashSet
        (requests.size()); + for(Request req: requests) { + if(req.type == Request.SUSPECT) + suspectedMembers.add(req.mbr); + } + + if(suspectedMembers.isEmpty()) + return; + + for(Address mbr: suspectedMembers) { + if(!suspected_mbrs.contains(mbr)) + suspected_mbrs.addElement(mbr); + } + + if(log.isDebugEnabled()) + log.debug("suspected members=" + suspectedMembers + ", suspected_mbrs=" + suspected_mbrs); + + if(wouldIBeCoordinator()) { + if(log.isDebugEnabled()) + log.debug("members are " + gms.members + ", coord=" + gms.local_addr + ": I'm the new coord !"); + + suspected_mbrs.removeAllElements(); + gms.becomeCoordinator(); + for(Address mbr: suspectedMembers) { + gms.getViewHandler().add(new Request(Request.SUSPECT, mbr, true, null)); + gms.ack_collector.suspect(mbr); + } + } + } + + + /** + * If we are leaving, we have to wait for the view change (last msg in the current view) that + * excludes us before we can leave. + * @param new_view The view to be installed + * @param digest If view is a MergeView, digest contains the seqno digest of all members and has to + * be set by GMS + */ + public void handleViewChange(View new_view, Digest digest) { + Vector mbrs=new_view.getMembers(); + if(log.isDebugEnabled()) log.debug("view=" + new_view); + suspected_mbrs.removeAllElements(); + if(leaving && !mbrs.contains(gms.local_addr)) { // received a view in which I'm not member: ignore + return; + } + gms.installView(new_view, digest); + } + + +/* public void handleSuspect(Address mbr) { + if(mbr == null) return; + if(!suspected_mbrs.contains(mbr)) + suspected_mbrs.addElement(mbr); + + if(log.isDebugEnabled()) log.debug("suspected mbr=" + mbr + ", suspected_mbrs=" + suspected_mbrs); + + if(wouldIBeCoordinator()) { + if(log.isDebugEnabled()) log.debug("suspected mbr=" + mbr + "), members are " + + gms.members + ", coord=" + gms.local_addr + ": I'm the new coord !"); + + suspected_mbrs.removeAllElements(); + gms.becomeCoordinator(); + // gms.getImpl().suspect(mbr); + gms.getViewHandler().add(new Request(Request.SUSPECT, mbr, true, null)); + gms.ack_collector.suspect(mbr); + } + }*/ + + public void handleMergeRequest(Address sender, ViewId merge_id) { + // only coords handle this method; reject it if we're not coord + sendMergeRejectedResponse(sender, merge_id); + } + + /* ---------------------------------- Private Methods --------------------------------------- */ + + /** + * Determines whether this member is the new coordinator given a list of suspected members. This is + * computed as follows: the list of currently suspected members (suspected_mbrs) is removed from the current + * membership. If the first member of the resulting list is equals to the local_addr, then it is true, + * otherwise false. Example: own address is B, current membership is {A, B, C, D}, suspected members are {A, + * D}. The resulting list is {B, C}. The first member of {B, C} is B, which is equal to the + * local_addr. Therefore, true is returned. + */ + boolean wouldIBeCoordinator() { + Address new_coord; + Vector
        mbrs=gms.members.getMembers(); // getMembers() returns a *copy* of the membership vector + mbrs.removeAll(suspected_mbrs); + if(mbrs.size() < 1) return false; + new_coord=mbrs.firstElement(); + return gms.local_addr.equals(new_coord); + } + + + void sendLeaveMessage(Address coord, Address mbr) { + Message msg=new Message(coord, null, null); + msg.setFlag(Message.OOB); + GMS.GmsHeader hdr=new GMS.GmsHeader(GMS.GmsHeader.LEAVE_REQ, mbr); + + msg.putHeader(gms.getName(), hdr); + gms.getDownProtocol().down(new Event(Event.MSG, msg)); + } + + + /* ------------------------------ End of Private Methods ------------------------------------ */ + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/STABLE.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/STABLE.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/STABLE.java 17 Aug 2012 14:51:23 -0000 1.1 @@ -0,0 +1,874 @@ +package org.jgroups.protocols.pbcast; + + +import org.jgroups.*; +import org.jgroups.annotations.GuardedBy; +import org.jgroups.stack.Protocol; +import org.jgroups.util.*; + +import java.io.*; +import java.util.*; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + + +/** + * Computes the broadcast messages that are stable; i.e., have been received by all members. Sends + * STABLE events up the stack when this is the case. This allows NAKACK to garbage collect messages that + * have been seen by all members.

        + * Works as follows: periodically we mcast our highest seqnos (seen for each member) to the group. + * A stability vector, which maintains the highest seqno for each member and initially contains no data, + * is updated when such a message is received. The entry for a member P is computed set to + * min(entry[P], digest[P]). When messages from all members have been received, a stability + * message is mcast, which causes all members to send a STABLE event up the stack (triggering garbage collection + * in the NAKACK layer).

        + * New: when max_bytes is exceeded (unless disabled by setting it to 0), + * a STABLE task will be started (unless it is already running). Design in docs/design/STABLE.txt + * @author Bela Ban + * @version $Id: STABLE.java,v 1.1 2012/08/17 14:51:23 marcin Exp $ + */ +public class STABLE extends Protocol { + private Address local_addr=null; + private final Set

        mbrs=new LinkedHashSet
        (); // we don't need ordering here + + @GuardedBy("lock") + private final MutableDigest digest=new MutableDigest(10); // keeps track of the highest seqnos from all members + + /** Keeps track of who we already heard from (STABLE_GOSSIP msgs). This is cleared initially, and we + * add the sender when a STABLE message is received. When the list is full (responses from all members), + * we send a STABILITY message */ + @GuardedBy("lock") + private final Set
        votes=new HashSet
        (); + + + private final Lock lock=new ReentrantLock(); + + /** Sends a STABLE gossip every 20 seconds on average. 0 disables gossipping of STABLE messages */ + private long desired_avg_gossip=20000; + + /** delay before we send STABILITY msg (give others a change to send first). This should be set to a very + * small number (> 0 !) if max_bytes is used */ + private long stability_delay=6000; + + @GuardedBy("stability_lock") + private Future stability_task_future=null; + private final Lock stability_lock=new ReentrantLock(); // to synchronize on stability_task + + @GuardedBy("stable_task_lock") + private Future stable_task_future=null; // bcasts periodic STABLE message (added to timer below) + private final Lock stable_task_lock=new ReentrantLock(); // to sync on stable_task + + + private TimeScheduler timer=null; // to send periodic STABLE msgs (and STABILITY messages) + private static final String name="STABLE"; + + /** Total amount of bytes from incoming messages (default = 0 = disabled). When exceeded, a STABLE + * message will be broadcast and num_bytes_received reset to 0 . If this is > 0, then ideally + * stability_delay should be set to a low number as well */ + private long max_bytes=0; + + /** The total number of bytes received from unicast and multicast messages */ + @GuardedBy("received") + private long num_bytes_received=0; + + private final Lock received=new ReentrantLock(); + + /** When true, don't take part in garbage collection protocol: neither send STABLE messages nor + * handle STABILITY messages */ + private boolean suspended=false; + + private boolean initialized=false; + + private Future resume_task_future=null; + private final Object resume_task_mutex=new Object(); + + private int num_stable_msgs_sent=0; + private int num_stable_msgs_received=0; + private int num_stability_msgs_sent=0; + private int num_stability_msgs_received=0; + + private static final long MAX_SUSPEND_TIME=200000; + + + public String getName() { + return name; + } + + public long getDesiredAverageGossip() { + return desired_avg_gossip; + } + + public void setDesiredAverageGossip(long gossip_interval) { + desired_avg_gossip=gossip_interval; + } + + public long getMaxBytes() { + return max_bytes; + } + + public void setMaxBytes(long max_bytes) { + this.max_bytes=max_bytes; + } + + public long getBytes() {return num_bytes_received;} + public int getStableSent() {return num_stable_msgs_sent;} + public int getStableReceived() {return num_stable_msgs_received;} + public int getStabilitySent() {return num_stability_msgs_sent;} + public int getStabilityReceived() {return num_stability_msgs_received;} + + + public void resetStats() { + super.resetStats(); + num_stability_msgs_received=num_stability_msgs_sent=num_stable_msgs_sent=num_stable_msgs_received=0; + } + + + public Vector requiredDownServices() { + Vector retval=new Vector(); + retval.addElement(Event.GET_DIGEST); // NAKACK layer + return retval; + } + + public boolean setProperties(Properties props) { + String str; + + super.setProperties(props); + str=props.getProperty("digest_timeout"); + if(str != null) { + props.remove("digest_timeout"); + log.error("digest_timeout has been deprecated; it will be ignored"); + } + + str=props.getProperty("desired_avg_gossip"); + if(str != null) { + desired_avg_gossip=Long.parseLong(str); + props.remove("desired_avg_gossip"); + } + + str=props.getProperty("stability_delay"); + if(str != null) { + stability_delay=Long.parseLong(str); + props.remove("stability_delay"); + } + + str=props.getProperty("max_gossip_runs"); + if(str != null) { + props.remove("max_gossip_runs"); + log.error("max_gossip_runs has been deprecated and will be ignored"); + } + + str=props.getProperty("max_bytes"); + if(str != null) { + max_bytes=Long.parseLong(str); + props.remove("max_bytes"); + } + + str=props.getProperty("max_suspend_time"); + if(str != null) { + log.error("max_suspend_time is not supported any longer; please remove it (ignoring it)"); + props.remove("max_suspend_time"); + } + + Util.checkBufferSize("STABLE.max_bytes", max_bytes); + + if(!props.isEmpty()) { + log.error("these properties are not recognized: " + props); + return false; + } + return true; + } + + + private void suspend(long timeout) { + if(!suspended) { + suspended=true; + if(log.isDebugEnabled()) + log.debug("suspending message garbage collection"); + } + startResumeTask(timeout); // will not start task if already running + } + + private void resume() { + lock.lock(); + try { + resetDigest(); // start from scratch + suspended=false; + } + finally { + lock.unlock(); + } + + if(log.isDebugEnabled()) + log.debug("resuming message garbage collection"); + stopResumeTask(); + } + + public void start() throws Exception { + timer=getTransport().getTimer(); + if(timer == null) + throw new Exception("timer cannot be retrieved from protocol stack"); + if(desired_avg_gossip > 0) + startStableTask(); + } + + public void stop() { + stopStableTask(); + } + + + public Object up(Event evt) { + Message msg; + StableHeader hdr; + int type=evt.getType(); + + switch(type) { + + case Event.MSG: + msg=(Message)evt.getArg(); + hdr=(StableHeader)msg.getHeader(name); + if(hdr == null) { + handleRegularMessage(msg); + return up_prot.up(evt); + } + + switch(hdr.type) { + case StableHeader.STABLE_GOSSIP: + handleStableMessage(msg.getSrc(), hdr.stableDigest); + break; + case StableHeader.STABILITY: + handleStabilityMessage(hdr.stableDigest, msg.getSrc()); + break; + default: + if(log.isErrorEnabled()) log.error("StableHeader type " + hdr.type + " not known"); + } + return null; // don't pass STABLE or STABILITY messages up the stack + + case Event.VIEW_CHANGE: + Object retval=up_prot.up(evt); + View view=(View)evt.getArg(); + handleViewChange(view); + return retval; + + case Event.SET_LOCAL_ADDRESS: + local_addr=(Address)evt.getArg(); + break; + } + return up_prot.up(evt); + } + + + + private void handleRegularMessage(Message msg) { + // only if message counting is enabled, and only for multicast messages + // fixes http://jira.jboss.com/jira/browse/JGRP-233 + if(max_bytes <= 0) + return; + Address dest=msg.getDest(); + if(dest == null || dest.isMulticastAddress()) { + received.lock(); + boolean locked=true; + try { + num_bytes_received+=(long)msg.getLength(); + if(num_bytes_received >= max_bytes) { + if(log.isTraceEnabled()) { + log.trace(new StringBuilder("max_bytes has been reached (").append(max_bytes). + append(", bytes received=").append(num_bytes_received).append("): triggers stable msg")); + } + num_bytes_received=0; + received.unlock(); + locked=false; + Digest my_digest=getDigest(); // asks the NAKACK protocol for the current digest, + if(log.isTraceEnabled()) + log.trace("setting latest_local_digest from NAKACK: " + my_digest.printHighestDeliveredSeqnos()); + sendStableMessage(my_digest); + } + } + finally { + if(locked) + received.unlock(); + } + } + } + + + public Object down(Event evt) { + switch(evt.getType()) { + case Event.VIEW_CHANGE: + Object retval=down_prot.down(evt); + View v=(View)evt.getArg(); + handleViewChange(v); + return retval; + + case Event.SUSPEND_STABLE: + long timeout=0; + Object t=evt.getArg(); + if(t != null && t instanceof Long) + timeout=(Long)t; + suspend(timeout); + break; + + case Event.RESUME_STABLE: + resume(); + break; + } + return down_prot.down(evt); + } + + + public void runMessageGarbageCollection() { + Digest copy=getDigest(); + sendStableMessage(copy); + } + + + + /* --------------------------------------- Private Methods ---------------------------------------- */ + + + private void handleViewChange(View v) { + Vector
        tmp=v.getMembers(); + synchronized(mbrs) { + mbrs.clear(); + mbrs.addAll(tmp); + } + lock.lock(); + try { + resetDigest(); + if(!initialized) + initialized=true; + } + finally { + lock.unlock(); + } + } + + + + + /** Update my own digest from a digest received by somebody else. Returns whether the update was successful. + * Needs to be called with a lock on digest */ + @GuardedBy("lock") + private boolean updateLocalDigest(Digest d, Address sender) { + if(d == null || d.size() == 0) + return false; + + if(!initialized) { + if(log.isTraceEnabled()) + log.trace("STABLE message will not be handled as I'm not yet initialized"); + return false; + } + + if(!digest.sameSenders(d)) { + // to avoid sending incorrect stability/stable msgs, we simply reset our votes list, see DESIGN + resetDigest(); + return false; + } + + StringBuilder sb=null; + if(log.isTraceEnabled()) { + sb=new StringBuilder("[").append(local_addr).append("] handling digest from ").append(sender).append(" ("). + append(votes.size()).append(" votes):\nmine: ").append(digest.printHighestDeliveredSeqnos()) + .append("\nother: ").append(d.printHighestDeliveredSeqnos()); + } + Address mbr; + long highest_seqno, my_highest_seqno, new_highest_seqno, my_low, low, new_low; + long highest_seen_seqno, my_highest_seen_seqno, new_highest_seen_seqno; + Digest.Entry val; + for(Map.Entry entry: d.getSenders().entrySet()) { + mbr=entry.getKey(); + val=entry.getValue(); + low=val.getLow(); + highest_seqno=val.getHighestDeliveredSeqno(); // highest *delivered* seqno + highest_seen_seqno=val.getHighestReceivedSeqno(); // highest *received* seqno + + my_low=digest.lowSeqnoAt(mbr); + new_low=Math.min(my_low, low); + + // compute the minimum of the highest seqnos deliverable (for garbage collection) + my_highest_seqno=digest.highestDeliveredSeqnoAt(mbr); + // compute the maximum of the highest seqnos seen (for retransmission of last missing message) + my_highest_seen_seqno=digest.highestReceivedSeqnoAt(mbr); + + new_highest_seqno=Math.min(my_highest_seqno, highest_seqno); + new_highest_seen_seqno=Math.max(my_highest_seen_seqno, highest_seen_seqno); + digest.setHighestDeliveredAndSeenSeqnos(mbr, new_low, new_highest_seqno, new_highest_seen_seqno); + } + if(log.isTraceEnabled()) { + assert sb != null; + sb.append("\nresult: ").append(digest.printHighestDeliveredSeqnos()).append("\n"); + log.trace(sb); + } + return true; + } + + + @GuardedBy("lock") + private void resetDigest() { + Digest tmp=getDigest(); + digest.replace(tmp); + if(log.isTraceEnabled()) + log.trace("resetting digest from NAKACK: " + digest.printHighestDeliveredSeqnos()); + votes.clear(); + } + + /** + * Adds mbr to votes and returns true if we have all the votes, otherwise false. + * @param mbr + */ + @GuardedBy("lock") + private boolean addVote(Address mbr) { + boolean added=votes.add(mbr); + return added && allVotesReceived(votes); + } + + /** Votes is already locked and guaranteed to be non-null */ + private boolean allVotesReceived(Set
        votes) { + synchronized(mbrs) { + return votes.equals(mbrs); // compares identity, size and element-wise (if needed) + } + } + + + private void startStableTask() { + stable_task_lock.lock(); + try { + if(stable_task_future == null || stable_task_future.isDone()) { + StableTask stable_task=new StableTask(); + stable_task_future=timer.scheduleWithDynamicInterval(stable_task, true); + if(log.isTraceEnabled()) + log.trace("stable task started"); + } + } + finally { + stable_task_lock.unlock(); + } + } + + + private void stopStableTask() { + stable_task_lock.lock(); + try { + if(stable_task_future != null) { + stable_task_future.cancel(false); + stable_task_future=null; + } + } + finally { + stable_task_lock.unlock(); + } + } + + + private void startResumeTask(long max_suspend_time) { + max_suspend_time=(long)(max_suspend_time * 1.1); // little slack + if(max_suspend_time <= 0) + max_suspend_time=MAX_SUSPEND_TIME; + + synchronized(resume_task_mutex) { + if(resume_task_future == null || resume_task_future.isDone()) { + ResumeTask resume_task=new ResumeTask(); + resume_task_future=timer.schedule(resume_task, max_suspend_time, TimeUnit.MILLISECONDS); // fixed-rate scheduling + if(log.isDebugEnabled()) + log.debug("resume task started, max_suspend_time=" + max_suspend_time); + } + } + + } + + + private void stopResumeTask() { + synchronized(resume_task_mutex) { + if(resume_task_future != null) { + resume_task_future.cancel(false); + resume_task_future=null; + } + } + } + + + private void startStabilityTask(Digest d, long delay) { + stability_lock.lock(); + try { + if(stability_task_future == null || stability_task_future.isDone()) { + StabilitySendTask stability_task=new StabilitySendTask(d); // runs only once + stability_task_future=timer.schedule(stability_task, delay, TimeUnit.MILLISECONDS); + } + } + finally { + stability_lock.unlock(); + } + } + + + private void stopStabilityTask() { + stability_lock.lock(); + try { + if(stability_task_future != null) { + stability_task_future.cancel(false); + stability_task_future=null; + } + } + finally { + stability_lock.unlock(); + } + } + + + /** + Digest d contains (a) the highest seqnos deliverable for each sender and (b) the highest seqnos + seen for each member. (Difference: with 1,2,4,5, the highest seqno seen is 5, whereas the highest + seqno deliverable is 2). The minimum of all highest seqnos deliverable will be taken to send a stability + message, which results in garbage collection of messages lower than the ones in the stability vector. The + maximum of all seqnos will be taken to trigger possible retransmission of last missing seqno (see DESIGN + for details). + */ + private void handleStableMessage(Address sender, Digest d) { + if(d == null || sender == null) { + if(log.isErrorEnabled()) log.error("digest or sender is null"); + return; + } + + if(!initialized) { + if(log.isTraceEnabled()) + log.trace("STABLE message will not be handled as I'm not yet initialized"); + return; + } + + if(suspended) { + if(log.isTraceEnabled()) + log.trace("STABLE message will not be handled as I'm suspended"); + return; + } + + Digest copy=null; + boolean all_votes_received=false; + lock.lock(); + try { + if(votes.contains(sender)) // already received gossip from sender; discard it + return; + num_stable_msgs_received++; + boolean success=updateLocalDigest(d, sender); + if(!success) // we can only add the sender to votes if *all* elements of my digest were updated + return; + + all_votes_received=addVote(sender); + if(all_votes_received) + copy=digest.copy(); + } + finally { + lock.unlock(); + } + + // we don't yet reset digest: new STABLE messages will be discarded anyway as we have already + // received votes from their senders + if(copy != null) { + sendStabilityMessage(copy); + } + } + + + private void handleStabilityMessage(Digest stable_digest, Address sender) { + if(stable_digest == null) { + if(log.isErrorEnabled()) log.error("stability digest is null"); + return; + } + + if(!initialized) { + if(log.isTraceEnabled()) + log.trace("STABLE message will not be handled as I'm not yet initialized"); + return; + } + + if(suspended) { + if(log.isDebugEnabled()) { + log.debug("stability message will not be handled as I'm suspended"); + } + return; + } + + if(log.isTraceEnabled()) + log.trace(new StringBuilder("received stability msg from ").append(sender).append(": ").append(stable_digest.printHighestDeliveredSeqnos())); + stopStabilityTask(); + + lock.lock(); + try { + // we won't handle the gossip d, if d's members don't match the membership in my own digest, + // this is part of the fix for the NAKACK problem (bugs #943480 and #938584) + if(!this.digest.sameSenders(stable_digest)) { + if(log.isDebugEnabled()) { + log.debug("received digest (digest=" + stable_digest + ") which does not match my own digest ("+ + this.digest + "): ignoring digest and re-initializing own digest"); + } + resetDigest(); + return; + } + num_stability_msgs_received++; + resetDigest(); + } + finally { + lock.unlock(); + } + + // pass STABLE event down the stack, so NAKACK can garbage collect old messages + down_prot.down(new Event(Event.STABLE, stable_digest)); + } + + + + + /** + * Bcasts a STABLE message of the current digest to all members. Message contains highest seqnos of all members + * seen by this member. Highest seqnos are retrieved from the NAKACK layer below. + * @param d A copy of this.digest + */ + private void sendStableMessage(Digest d) { + if(suspended) { + if(log.isTraceEnabled()) + log.trace("will not send STABLE message as I'm suspended"); + return; + } + + if(d != null && d.size() > 0) { + if(log.isTraceEnabled()) + log.trace("sending stable msg " + d.printHighestDeliveredSeqnos()); + num_stable_msgs_sent++; + final Message msg=new Message(); // mcast message + msg.setFlag(Message.OOB); + StableHeader hdr=new StableHeader(StableHeader.STABLE_GOSSIP, d); + msg.putHeader(name, hdr); + + Runnable r=new Runnable() { + public void run() { + down_prot.down(new Event(Event.MSG, msg)); + } + }; + + // Run in a separate thread so we don't potentially block (http://jira.jboss.com/jira/browse/JGRP-532) + timer.execute(r); + // down_prot.down(new Event(Event.MSG, msg)); + } + } + + + + /** + Schedules a stability message to be mcast after a random number of milliseconds (range 1-5 secs). + The reason for waiting a random amount of time is that, in the worst case, all members receive a + STABLE_GOSSIP message from the last outstanding member at the same time and would therefore mcast the + STABILITY message at the same time too. To avoid this, each member waits random N msecs. If, before N + elapses, some other member sent the STABILITY message, we just cancel our own message. If, during + waiting for N msecs to send STABILITY message S1, another STABILITY message S2 is to be sent, we just + discard S2. + @param tmp A copy of te stability digest, so we don't need to copy it again + */ + private void sendStabilityMessage(Digest tmp) { + long delay; + + if(suspended) { + if(log.isTraceEnabled()) + log.trace("STABILITY message will not be sent as I'm suspended"); + return; + } + + // give other members a chance to mcast STABILITY message. if we receive STABILITY by the end of + // our random sleep, we will not send the STABILITY msg. this prevents that all mbrs mcast a + // STABILITY msg at the same time + delay=Util.random(stability_delay); + if(log.isTraceEnabled()) log.trace("sending stability msg (in " + delay + " ms) " + tmp.printHighestDeliveredSeqnos() + + " (copy=" + tmp.hashCode() + ")"); + startStabilityTask(tmp, delay); + } + + + private Digest getDigest() { + return (Digest)down_prot.down(Event.GET_DIGEST_EVT); + } + + + /* ------------------------------------End of Private Methods ------------------------------------- */ + + + + + + + + public static class StableHeader extends Header implements Streamable { + public static final int STABLE_GOSSIP=1; + public static final int STABILITY=2; + + int type=0; + // Digest digest=new Digest(); // used for both STABLE_GOSSIP and STABILITY message + Digest stableDigest=null; // changed by Bela April 4 2004 + + public StableHeader() { + } // used for externalizable + + + public StableHeader(int type, Digest digest) { + this.type=type; + this.stableDigest=digest; + } + + + static String type2String(int t) { + switch(t) { + case STABLE_GOSSIP: + return "STABLE_GOSSIP"; + case STABILITY: + return "STABILITY"; + default: + return ""; + } + } + + public String toString() { + StringBuilder sb=new StringBuilder(); + sb.append('['); + sb.append(type2String(type)); + sb.append("]: digest is "); + sb.append(stableDigest); + return sb.toString(); + } + + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeInt(type); + if(stableDigest == null) { + out.writeBoolean(false); + return; + } + out.writeBoolean(true); + stableDigest.writeExternal(out); + } + + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + type=in.readInt(); + boolean digest_not_null=in.readBoolean(); + if(digest_not_null) { + stableDigest=new Digest(); + stableDigest.readExternal(in); + } + } + + public int size() { + int retval=Global.INT_SIZE + Global.BYTE_SIZE; // type + presence for digest + if(stableDigest != null) + retval+=stableDigest.serializedSize(); + return retval; + } + + public void writeTo(DataOutputStream out) throws IOException { + out.writeInt(type); + Util.writeStreamable(stableDigest, out); + } + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + type=in.readInt(); + stableDigest=(Digest)Util.readStreamable(Digest.class, in); + } + + + } + + + + + /** + Mcast periodic STABLE message. Interval between sends varies. + */ + private class StableTask implements TimeScheduler.Task { + + public long nextInterval() { + long interval=computeSleepTime(); + if(interval <= 0) + return 10000; + else + return interval; + } + + + public void run() { + if(suspended) { + if(log.isTraceEnabled()) + log.trace("stable task will not run as suspended=" + suspended); + return; + } + + // asks the NAKACK protocol for the current digest + Digest my_digest=getDigest(); + if(my_digest == null) { + if(log.isWarnEnabled()) + log.warn("received null digest, skipped sending of stable message"); + return; + } + if(log.isTraceEnabled()) + log.trace("setting latest_local_digest from NAKACK: " + my_digest.printHighestDeliveredSeqnos()); + sendStableMessage(my_digest); + } + + long computeSleepTime() { + return getRandom((mbrs.size() * desired_avg_gossip * 2)); + } + + long getRandom(long range) { + return (long)((Math.random() * range) % range); + } + } + + + + + + /** + * Multicasts a STABILITY message. + */ + private class StabilitySendTask implements Runnable { + Digest stability_digest=null; + + StabilitySendTask(Digest d) { + this.stability_digest=d; + } + + public void run() { + Message msg; + StableHeader hdr; + + if(suspended) { + if(log.isDebugEnabled()) { + log.debug("STABILITY message will not be sent as suspended=" + suspended); + } + return; + } + + if(stability_digest != null) { + msg=new Message(); + msg.setFlag(Message.OOB); + hdr=new StableHeader(StableHeader.STABILITY, stability_digest); + msg.putHeader(STABLE.name, hdr); + if(log.isTraceEnabled()) log.trace("sending stability msg " + stability_digest.printHighestDeliveredSeqnos() + + " (copy=" + stability_digest.hashCode() + ")"); + num_stability_msgs_sent++; + down_prot.down(new Event(Event.MSG, msg)); + } + } + } + + + private class ResumeTask implements Runnable { + ResumeTask() { + } + + public void run() { + if(suspended) + log.warn("ResumeTask resumed message garbage collection - this should be done by a RESUME_STABLE event; " + + "check why this event was not received (or increase max_suspend_time for large state transfers)"); + resume(); + } + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/STATE_TRANSFER.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/STATE_TRANSFER.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/STATE_TRANSFER.java 17 Aug 2012 14:51:22 -0000 1.1 @@ -0,0 +1,580 @@ + +package org.jgroups.protocols.pbcast; + +import org.jgroups.*; +import org.jgroups.stack.Protocol; +import org.jgroups.stack.StateTransferInfo; +import org.jgroups.util.Streamable; +import org.jgroups.util.Util; +import org.jgroups.util.Digest; + +import java.io.*; +import java.util.*; + + +/** + * New STATE_TRANSFER protocol based on PBCAST. Compared to the one in + * ./protocols, it doesn't need a QUEUE layer above it. A state request is sent + * to a chosen member (coordinator if null). That member makes a copy D of its + * current digest and asks the application for a copy of its current state S. + * Then the member returns both S and D to the requester. The requester first + * sets its digest to D and then returns the state to the application. + * + * @author Bela Ban + * @version $Id: STATE_TRANSFER.java,v 1.1 2012/08/17 14:51:22 marcin Exp $ + */ +public class STATE_TRANSFER extends Protocol { + Address local_addr=null; + final Vector
        members=new Vector
        (); + long state_id=1; // used to differentiate between state transfers (not currently used) + + /** Map of state requesters. Keys are state IDs, values are Sets of Addresses (one for each requester) */ + final Map> state_requesters=new HashMap>(); + + /** set to true while waiting for a STATE_RSP */ + boolean waiting_for_state_response=false; + + final Map map=new HashMap(); // to store configuration information + long start, stop; // to measure state transfer time + int num_state_reqs=0; + long num_bytes_sent=0; + double avg_state_size=0; + final static String name="STATE_TRANSFER"; + boolean flushProtocolInStack = false; + + + /** All protocol names have to be unique ! */ + public String getName() { + return name; + } + + public int getNumberOfStateRequests() {return num_state_reqs;} + public long getNumberOfStateBytesSent() {return num_bytes_sent;} + public double getAverageStateSize() {return avg_state_size;} + + public Vector requiredDownServices() { + Vector retval=new Vector(); + retval.addElement(new Integer(Event.GET_DIGEST)); + retval.addElement(new Integer(Event.SET_DIGEST)); + return retval; + } + + public void resetStats() { + super.resetStats(); + num_state_reqs=0; + num_bytes_sent=0; + avg_state_size=0; + } + + + public boolean setProperties(Properties props) { + super.setProperties(props); + String str=props.getProperty("use_flush"); + if(str != null) { + log.warn("use_flush has been deprecated and its value will be ignored"); + props.remove("use_flush"); + } + str=props.getProperty("flush_timeout"); + if(str != null) { + log.warn("flush_timeout has been deprecated and its value will be ignored"); + props.remove("flush_timeout"); + } + + if(!props.isEmpty()) { + log.error("the following properties are not recognized: " + props); + return false; + } + return true; + } + + public void init() throws Exception { + map.put("state_transfer", Boolean.TRUE); + map.put("protocol_class", getClass().getName()); + } + + + public void start() throws Exception { + up_prot.up(new Event(Event.CONFIG, map)); + } + + public void stop() { + super.stop(); + waiting_for_state_response=false; + } + + + public Object up(Event evt) { + switch(evt.getType()) { + + case Event.MSG: + Message msg=(Message)evt.getArg(); + StateHeader hdr=(StateHeader)msg.getHeader(name); + if(hdr == null) + break; + + switch(hdr.type) { + case StateHeader.STATE_REQ: + handleStateReq(hdr); + break; + case StateHeader.STATE_RSP: + handleStateRsp(hdr, msg.getBuffer()); + break; + default: + if(log.isErrorEnabled()) log.error("type " + hdr.type + " not known in StateHeader"); + break; + } + return null; + + case Event.SET_LOCAL_ADDRESS: + local_addr=(Address)evt.getArg(); + break; + + case Event.TMP_VIEW: + case Event.VIEW_CHANGE: + handleViewChange((View)evt.getArg()); + break; + + case Event.CONFIG : + Map config=(Map)evt.getArg(); + if(config != null && config.containsKey("state_transfer")) { + log.error("Protocol stack cannot contain two state transfer protocols. Remove either one of them"); + } + break; + } + return up_prot.up(evt); + } + + + + public Object down(Event evt) { + switch(evt.getType()) { + + case Event.TMP_VIEW: + case Event.VIEW_CHANGE: + handleViewChange((View)evt.getArg()); + break; + + // generated by JChannel.getState(). currently, getting the state from more than 1 mbr is not implemented + case Event.GET_STATE: + Address target; + StateTransferInfo info=(StateTransferInfo)evt.getArg(); + if(info.target == null) { + target=determineCoordinator(); + } + else { + target=info.target; + if(target.equals(local_addr)) { + if(log.isErrorEnabled()) log.error("GET_STATE: cannot fetch state from myself !"); + target=null; + } + } + if(target == null) { + if(log.isDebugEnabled()) log.debug("GET_STATE: first member (no state)"); + up_prot.up(new Event(Event.GET_STATE_OK, new StateTransferInfo())); + } + else { + Message state_req=new Message(target, null, null); + state_req.putHeader(name, new StateHeader(StateHeader.STATE_REQ, local_addr, state_id++, null, info.state_id)); + if(log.isDebugEnabled()) log.debug("GET_STATE: asking " + target + " for state"); + + // suspend sending and handling of message garbage collection gossip messages, + // fixes bugs #943480 and #938584). Wake up when state has been received + if(log.isDebugEnabled()) + log.debug("passing down a SUSPEND_STABLE event"); + down_prot.down(new Event(Event.SUSPEND_STABLE, new Long(info.timeout))); + waiting_for_state_response=true; + start=System.currentTimeMillis(); + down_prot.down(new Event(Event.MSG, state_req)); + } + return null; // don't pass down any further ! + + case Event.CONFIG : + Map config = (Map) evt.getArg(); + if(config != null && config.containsKey("flush_supported")){ + flushProtocolInStack = true; + } + break; + + } + + return down_prot.down(evt); // pass on to the layer below us + } + + + + + + + + + + /* --------------------------- Private Methods -------------------------------- */ + + /** + * When FLUSH is used we do not need to pass digests between members + * + * see JGroups/doc/design/PartialStateTransfer.txt see + * JGroups/doc/design/FLUSH.txt + * + * @return true if use of digests is required, false otherwise + */ + private boolean isDigestNeeded() { + return !flushProtocolInStack; + } + + private void requestApplicationStates(Address requester, Digest digest, boolean open_barrier) { + Set appl_ids=new HashSet(state_requesters.keySet()); + + List responses=new LinkedList(); + for(Iterator it=appl_ids.iterator(); it.hasNext();) { + String id=it.next(); + StateTransferInfo info=new StateTransferInfo(requester, id, 0L, null); + StateTransferInfo rsp=(StateTransferInfo)up_prot.up(new Event(Event.GET_APPLSTATE, info)); + responses.add(rsp); + } + if(open_barrier) + down_prot.down(new Event(Event.OPEN_BARRIER)); + for(StateTransferInfo rsp: responses) { + sendApplicationStateResponse(rsp, digest); + } + } + + + private void sendApplicationStateResponse(StateTransferInfo rsp, Digest digest) { + byte[] state=rsp.state; + String id=rsp.state_id; + List responses=null; + + synchronized(state_requesters) { + if(state_requesters.isEmpty()) { + if(log.isWarnEnabled()) + log.warn("GET_APPLSTATE_OK: received application state, but there are no requesters !"); + return; + } + if(stats) { + num_state_reqs++; + if(state != null) + num_bytes_sent+=state.length; + avg_state_size=num_bytes_sent / num_state_reqs; + } + + Set
        requesters=state_requesters.get(id); + if(requesters == null || requesters.isEmpty()) { + log.warn("received state for id=" + id + ", but there are no requesters for this ID"); + } + else { + responses=new LinkedList(); + for(Iterator
        it=requesters.iterator(); it.hasNext();) { + Address requester=it.next(); + Message state_rsp=new Message(requester, null, state); + StateHeader hdr=new StateHeader(StateHeader.STATE_RSP, local_addr, 0, digest, id); + state_rsp.putHeader(name, hdr); + responses.add(state_rsp); + } + state_requesters.remove(id); + } + } + + if(responses != null && !responses.isEmpty()) { + for(Message state_rsp: responses) { + if(log.isTraceEnabled()) { + int length=state != null? state.length : 0; + log.trace("sending state for ID=" + id + " to " + state_rsp.getDest() + " (" + length + " bytes)"); + } + down_prot.down(new Event(Event.MSG, state_rsp)); + + // This has to be done in a separate thread, so we don't block on FC + // (see http://jira.jboss.com/jira/browse/JGRP-225 for details). This will be reverted once + // we have the threadless stack (http://jira.jboss.com/jira/browse/JGRP-181) + // and out-of-band messages (http://jira.jboss.com/jira/browse/JGRP-205) +// new Thread() { +// public void run() { +// down_prot.down(new Event(Event.MSG, state_rsp)); +// } +// }.start(); + // down_prot.down(new Event(Event.MSG, state_rsp)); + } + } + } + + + /** Return the first element of members which is not me. Otherwise return null. */ + private Address determineCoordinator() { + Address ret=null; + synchronized(members) { + if(members != null && members.size() > 1) { + for(int i=0; i < members.size(); i++) + if(!local_addr.equals(members.elementAt(i))) + return (Address)members.elementAt(i); + } + } + return ret; + } + + + private void handleViewChange(View v) { + Address old_coord; + Vector
        new_members=v.getMembers(); + boolean send_up_null_state_rsp=false; + + synchronized(members) { + old_coord=(!members.isEmpty()? members.firstElement() : null); + members.clear(); + members.addAll(new_members); + + // this handles the case where a coord dies during a state transfer; prevents clients from hanging forever + // Note this only takes a coordinator crash into account, a getState(target, timeout), where target is not + // null is not handled ! (Usually we get the state from the coordinator) + // http://jira.jboss.com/jira/browse/JGRP-148 + if(waiting_for_state_response && old_coord != null && !members.contains(old_coord)) { + send_up_null_state_rsp=true; + } + } + + if(send_up_null_state_rsp) { + if(log.isWarnEnabled()) + log.warn("discovered that the state provider (" + old_coord + ") crashed; will return null state to application"); + StateHeader hdr=new StateHeader(StateHeader.STATE_RSP, local_addr, 0, null, null); + handleStateRsp(hdr, null); // sends up null GET_STATE_OK + } + } + + /** + * If a state transfer is in progress, we don't need to send a GET_APPLSTATE event to the application, but + * instead we just add the sender to the requester list so it will receive the same state when done. If not, + * we add the sender to the requester list and send a GET_APPLSTATE event up. + */ + private void handleStateReq(StateHeader hdr) { + Address sender=hdr.sender; + if(sender == null) { + if(log.isErrorEnabled()) log.error("sender is null !"); + return; + } + + String id=hdr.state_id; // id could be null, which means get the entire state + synchronized(state_requesters) { + boolean empty=state_requesters.isEmpty(); + Set
        requesters=state_requesters.get(id); + if(requesters == null) { + requesters=new HashSet
        (); + state_requesters.put(id, requesters); + } + requesters.add(sender); + + if(!isDigestNeeded()) { // state transfer is in progress, digest was already requested + requestApplicationStates(sender, null, false); + } + else if(empty) { + if(!flushProtocolInStack) { + down_prot.down(new Event(Event.CLOSE_BARRIER)); + } + Digest digest=(Digest)down_prot.down(new Event(Event.GET_DIGEST)); + if(log.isDebugEnabled()) + log.debug("digest is " + digest + ", getting application state"); + try { + requestApplicationStates(sender, digest, !flushProtocolInStack); + } + catch(Throwable t) { + if(log.isErrorEnabled()) + log.error("failed getting state from application", t); + if(!flushProtocolInStack) { + down_prot.down(new Event(Event.OPEN_BARRIER)); + } + } + } + } + } + + + /** Set the digest and the send the state up to the application */ + void handleStateRsp(StateHeader hdr, byte[] state) { + Address sender=hdr.sender; + Digest tmp_digest=hdr.my_digest; + String id=hdr.state_id; + Address state_sender=hdr.sender; + + waiting_for_state_response=false; + if(isDigestNeeded()){ + if(tmp_digest == null) { + if(log.isWarnEnabled()) + log.warn("digest received from " + sender + " is null, skipping setting digest !"); + } + else + down_prot.down(new Event(Event.SET_DIGEST, tmp_digest)); // set the digest (e.g. in NAKACK) + } + stop=System.currentTimeMillis(); + + // resume sending and handling of message garbage collection gossip messages, + // fixes bugs #943480 and #938584). Wakes up a previously suspended message garbage + // collection protocol (e.g. STABLE) + if(log.isDebugEnabled()) + log.debug("passing down a RESUME_STABLE event"); + down_prot.down(new Event(Event.RESUME_STABLE)); + + if(state == null) { + if(log.isWarnEnabled()) + log.warn("state received from " + sender + " is null, will return null state to application"); + } + else + log.debug("received state, size=" + state.length + " bytes. Time=" + (stop-start) + " milliseconds"); + StateTransferInfo info=new StateTransferInfo(state_sender, id, 0L, state); + up_prot.up(new Event(Event.GET_STATE_OK, info)); + } + + /* ------------------------ End of Private Methods ------------------------------ */ + + + + /** + * Wraps data for a state request/response. Note that for a state response the actual state will not"; + } + } + + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeObject(sender); + out.writeLong(id); + out.writeByte(type); + out.writeObject(my_digest); + if(state_id == null) { + out.writeBoolean(false); + } + else { + out.writeBoolean(true); + out.writeUTF(state_id); + } + } + + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + sender=(Address)in.readObject(); + id=in.readLong(); + type=in.readByte(); + my_digest=(Digest)in.readObject(); + if(in.readBoolean()) + state_id=in.readUTF(); + } + + + + public void writeTo(DataOutputStream out) throws IOException { + out.writeByte(type); + out.writeLong(id); + Util.writeAddress(sender, out); + Util.writeStreamable(my_digest, out); + Util.writeString(state_id, out); + } + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + type=in.readByte(); + id=in.readLong(); + sender=Util.readAddress(in); + my_digest=(Digest)Util.readStreamable(Digest.class, in); + state_id=Util.readString(in); + } + + public int size() { + int retval=Global.LONG_SIZE + Global.BYTE_SIZE; // id and type + + retval+=Util.size(sender); + + retval+=Global.BYTE_SIZE; // presence byte for my_digest + if(my_digest != null) + retval+=my_digest.serializedSize(); + + retval+=Global.BYTE_SIZE; // presence byte for state_id + if(state_id != null) + retval+=state_id.length() +2; + return retval; + } + } +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/STREAMING_STATE_TRANSFER.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/STREAMING_STATE_TRANSFER.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/STREAMING_STATE_TRANSFER.java 17 Aug 2012 14:51:23 -0000 1.1 @@ -0,0 +1,1119 @@ +package org.jgroups.protocols.pbcast; + +import org.jgroups.*; +import org.jgroups.annotations.GuardedBy; +import org.jgroups.stack.IpAddress; +import org.jgroups.stack.Protocol; +import org.jgroups.stack.StateTransferInfo; +import org.jgroups.util.ShutdownRejectedExecutionHandler; +import org.jgroups.util.Streamable; +import org.jgroups.util.Util; +import org.jgroups.util.Digest; + +import java.io.*; +import java.net.*; +import java.util.*; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + +/** + * STREAMING_STATE_TRANSFER, as its name implies, allows a + * streaming state transfer between two channel instances. + *

        + * + * Major advantage of this approach is that transferring application state to a + * joining member of a group does not entail loading of the complete application + * state into memory. Application state, for example, might be located entirely + * on some form of disk based storage. The default STATE_TRANSFER + * requires this state to be loaded entirely into memory before being + * transferred to a group member while STREAMING_STATE_TRANSFER + * does not. Thus STREAMING_STATE_TRANSFER protocol is able to + * transfer application state that is very large (>1Gb) without a likelihood of + * such transfer resulting in OutOfMemoryException. + *

        + * + * STREAMING_STATE_TRANSFER allows use of either default channel + * transport or separate tcp sockets for state transfer. If firewalls are not a + * concern then separate tcp sockets should be used as they offer faster state + * transfer. Transport for state transfer is selected using + * use_default_transport boolean property. + *

        + * + * + * Channel instance can be configured with either + * STREAMING_STATE_TRANSFER or STATE_TRANSFER but not + * both protocols at the same time. + * + *

        + * + * In order to process streaming state transfer an application has to implement + * ExtendedMessageListener if it is using channel in a push style + * mode or it has to process StreamingSetStateEvent and + * StreamingGetStateEvent if it is using channel in a pull style + * mode. + * + * + * @author Vladimir Blagojevic + * @see org.jgroups.ExtendedMessageListener + * @see org.jgroups.StreamingGetStateEvent + * @see org.jgroups.StreamingSetStateEvent + * @see org.jgroups.protocols.pbcast.STATE_TRANSFER + * @since 2.4 + * + * @version $Id: STREAMING_STATE_TRANSFER.java,v 1.1 2012/08/17 14:51:23 marcin Exp $ + * + */ +public class STREAMING_STATE_TRANSFER extends Protocol { + + private final static String NAME = "STREAMING_STATE_TRANSFER"; + + /* + * ----------------------------- Properties --------------------------------------------------- + */ + + /* + * The interface (NIC) used to accept state requests + */ + private InetAddress bind_addr; + + /* + * The port listening for state requests. Default value of 0 binds to any + * (ephemeral) port + */ + private int bind_port = 0; + + /* + * Maximum number of pool threads serving state requests. Default is 5 + */ + private int max_pool = 5; + + /* + * Keep alive for pool threads serving state requests. Default is 20000 msec + */ + private long pool_thread_keep_alive = 20 * 1000; + + /* + * Buffer size for state transfer. Default is 8 KB + */ + private int socket_buffer_size = 8 * 1024; + + /* + * If default transport is used the total state buffer size before state producer is blocked. Default is 81920 bytes + */ + private int buffer_queue_size = 81920; + + /* + * If true default transport will be used for state transfer rather than + * seperate TCP sockets. Default is false. + */ + boolean use_default_transport = false; + + /* + * --------------------------------------------- JMX statistics ------------------------------- + */ + + private final AtomicInteger num_state_reqs = new AtomicInteger(0); + + private final AtomicLong num_bytes_sent = new AtomicLong(0); + + private volatile double avg_state_size = 0; + + /* + * --------------------------------------------- Fields --------------------------------------- + */ + + private Address local_addr = null; + + @GuardedBy("members") + private final Vector

        members; + + /* + * BlockingQueue to accept state transfer Message(s) if default transport + * is used. Only state recipient uses this queue + */ + private BlockingQueue stateQueue; + + /* + * Runnable that listens for state requests and spawns threads to serve + * those requests if socket transport is used + */ + private StateProviderThreadSpawner spawner; + + /* + * Set to true if FLUSH protocol is detected in protocol stack + */ + private AtomicBoolean flushProtocolInStack = new AtomicBoolean(false); + + public STREAMING_STATE_TRANSFER() { + members = new Vector
        (); + } + + public final String getName() { + return NAME; + } + + public int getNumberOfStateRequests() { + return num_state_reqs.get(); + } + + public long getNumberOfStateBytesSent() { + return num_bytes_sent.get(); + } + + public double getAverageStateSize() { + return avg_state_size; + } + + public Vector requiredDownServices() { + Vector retval = new Vector(); + retval.addElement(new Integer(Event.GET_DIGEST)); + retval.addElement(new Integer(Event.SET_DIGEST)); + return retval; + } + + public void resetStats() { + super.resetStats(); + num_state_reqs.set(0); + num_bytes_sent.set(0); + avg_state_size = 0; + } + + public boolean setProperties(Properties props) { + super.setProperties(props); + + String str = props.getProperty("use_flush"); + if (str != null) { + log.warn("use_flush has been deprecated and its value will be ignored"); + props.remove("use_flush"); + } + str = props.getProperty("flush_timeout"); + if (str != null) { + log.warn("flush_timeout has been deprecated and its value will be ignored"); + props.remove("flush_timeout"); + } + + str = props.getProperty("use_reading_thread"); + if (str != null) { + log.warn("use_reading_thread has been deprecated and its value will be ignored"); + props.remove("use_reading_thread"); + } + + try { + bind_addr = Util.parseBindAddress(props, "bind_addr"); + } catch (UnknownHostException e) { + log.error("(bind_addr): host " + e.getLocalizedMessage() + " not known"); + return false; + } + use_default_transport = Util.parseBoolean(props, "use_default_transport", false); + buffer_queue_size = Util.parseInt(props, "buffer_queue_size", 81920); + bind_port = Util.parseInt(props, "start_port", 0); + socket_buffer_size = Util.parseInt(props, "socket_buffer_size", 8 * 1024); // 8K + max_pool = Util.parseInt(props, "max_pool", 5); + pool_thread_keep_alive = Util.parseLong(props, "pool_thread_keep_alive", 1000 * 30); // 30sec + if (!props.isEmpty()) { + log.error("the following properties are not recognized: " + props); + + return false; + } + return true; + } + + public void init() throws Exception { + } + + public void start() throws Exception { + Map map = new HashMap(); + map.put("state_transfer", Boolean.TRUE); + map.put("protocol_class", getClass().getName()); + up_prot.up(new Event(Event.CONFIG, map)); + + if (use_default_transport) { + int size = buffer_queue_size / socket_buffer_size; + // idiot proof it + if (size <= 1) { + size = 10; + } + if (log.isDebugEnabled()) { + log.debug("buffer_queue_size=" + buffer_queue_size + ", socket_buffer_size=" + + socket_buffer_size + ", creating queue of size " + size); + } + stateQueue = new ArrayBlockingQueue(size); + } + } + + public void stop() { + super.stop(); + if (spawner != null) { + spawner.stop(); + } + } + + public Object up(Event evt) { + switch (evt.getType()) { + + case Event.MSG : + Message msg = (Message) evt.getArg(); + StateHeader hdr = (StateHeader) msg.getHeader(getName()); + if (hdr != null) { + switch (hdr.type) { + case StateHeader.STATE_REQ : + handleStateReq(hdr); + break; + case StateHeader.STATE_RSP : + handleStateRsp(hdr); + break; + case StateHeader.STATE_PART : + try { + stateQueue.put(msg); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + break; + default : + if (log.isErrorEnabled()) + log.error("type " + hdr.type + " not known in StateHeader"); + break; + } + return null; + } + break; + + case Event.SET_LOCAL_ADDRESS : + local_addr = (Address) evt.getArg(); + break; + + case Event.TMP_VIEW : + case Event.VIEW_CHANGE : + handleViewChange((View) evt.getArg()); + break; + + case Event.CONFIG : + Map config = (Map) evt.getArg(); + if (bind_addr == null && (config != null && config.containsKey("bind_addr"))) { + bind_addr = (InetAddress) config.get("bind_addr"); + if (log.isDebugEnabled()) + log.debug("using bind_addr from CONFIG event " + bind_addr); + } + if (config != null && config.containsKey("state_transfer")) { + log.error("Protocol stack must have only one state transfer protocol"); + } + break; + } + return up_prot.up(evt); + } + + public Object down(Event evt) { + + switch (evt.getType()) { + + case Event.TMP_VIEW : + case Event.VIEW_CHANGE : + handleViewChange((View) evt.getArg()); + break; + + case Event.GET_STATE : + StateTransferInfo info = (StateTransferInfo) evt.getArg(); + Address target; + if (info.target == null) { + target = determineCoordinator(); + } else { + target = info.target; + if (target.equals(local_addr)) { + if (log.isErrorEnabled()) + log.error("GET_STATE: cannot fetch state from myself !"); + target = null; + } + } + if (target == null) { + if (log.isDebugEnabled()) + log.debug("GET_STATE: first member (no state)"); + up_prot.up(new Event(Event.GET_STATE_OK, new StateTransferInfo())); + } else { + Message state_req = new Message(target, null, null); + state_req.putHeader(getName(), new StateHeader(StateHeader.STATE_REQ, + local_addr, info.state_id)); + if (log.isDebugEnabled()) + log.debug("GET_STATE: asking " + target + + " for state, passing down a SUSPEND_STABLE event, timeout=" + + info.timeout); + + down_prot.down(new Event(Event.SUSPEND_STABLE, new Long(info.timeout))); + down_prot.down(new Event(Event.MSG, state_req)); + } + return null; // don't pass down any further ! + + case Event.STATE_TRANSFER_INPUTSTREAM_CLOSED : + if (log.isDebugEnabled()) + log.debug("STATE_TRANSFER_INPUTSTREAM_CLOSED received,passing down a RESUME_STABLE event"); + + down_prot.down(new Event(Event.RESUME_STABLE)); + return null; + case Event.CONFIG : + Map config = (Map) evt.getArg(); + if (config != null && config.containsKey("flush_supported")) { + flushProtocolInStack.set(true); + } + break; + + } + + return down_prot.down(evt); // pass on to the layer below us + } + + /* + * --------------------------- Private Methods ------------------------------------------------ + */ + + /** + * When FLUSH is used we do not need to pass digests between members + * + * see JGroups/doc/design/PArtialStateTransfer.txt see + * JGroups/doc/design/FLUSH.txt + * + * @return true if use of digests is required, false otherwise + */ + private boolean isDigestNeeded() { + return !flushProtocolInStack.get(); + } + + private void respondToStateRequester(String id, Address stateRequester, boolean open_barrier) + throws IOException { + + // setup the plumbing if needed + if (spawner == null) { + ServerSocket serverSocket = Util.createServerSocket(bind_addr, bind_port); + spawner = new StateProviderThreadSpawner(setupThreadPool(), serverSocket); + Thread t = getThreadFactory().newThread(spawner, + "STREAMING_STATE_TRANSFER server socket acceptor"); + t.start(); + } + + Digest digest = isDigestNeeded() ? (Digest) down_prot.down(Event.GET_DIGEST_EVT) : null; + + Message state_rsp = new Message(stateRequester); + StateHeader hdr = new StateHeader(StateHeader.STATE_RSP, local_addr, use_default_transport + ? null + : spawner.getServerSocketAddress(), digest, id); + state_rsp.putHeader(getName(), hdr); + + if (log.isDebugEnabled()) + log.debug("Responding to state requester " + state_rsp.getDest() + " with address " + + (use_default_transport ? null : spawner.getServerSocketAddress()) + + " and digest " + digest); + down_prot.down(new Event(Event.MSG, state_rsp)); + if (stats) { + num_state_reqs.incrementAndGet(); + } + + if (open_barrier) + down_prot.down(new Event(Event.OPEN_BARRIER)); + + if (use_default_transport) { + openAndProvideOutputStreamToStateProvider(stateRequester, id); + } + } + + private ThreadPoolExecutor setupThreadPool() { + ThreadPoolExecutor threadPool = new ThreadPoolExecutor(0, max_pool, pool_thread_keep_alive, + TimeUnit.MILLISECONDS, new SynchronousQueue()); + + ThreadFactory factory = new ThreadFactory() { + public Thread newThread(final Runnable command) { + return getThreadFactory().newThread(command, "STREAMING_STATE_TRANSFER sender"); + } + }; + threadPool.setRejectedExecutionHandler(new ShutdownRejectedExecutionHandler(threadPool + .getRejectedExecutionHandler())); + threadPool.setThreadFactory(factory); + return threadPool; + } + + private Address determineCoordinator() { + synchronized (members) { + for (Address member : members) { + if (!local_addr.equals(member)) { + return member; + } + } + } + return null; + } + + private void handleViewChange(View v) { + Vector
        new_members = v.getMembers(); + synchronized (members) { + members.clear(); + members.addAll(new_members); + } + } + + private void handleStateReq(StateHeader hdr) { + Address sender = hdr.sender; + String id = hdr.state_id; + if (sender == null) { + if (log.isErrorEnabled()) + log.error("sender is null !"); + return; + } + + if (isDigestNeeded()) { + down_prot.down(new Event(Event.CLOSE_BARRIER)); + // drain (and block) incoming msgs until after state has been + // returned + } + try { + respondToStateRequester(id, sender, isDigestNeeded()); + } catch (Throwable t) { + if (log.isErrorEnabled()) + log.error("failed fetching state from application", t); + if (isDigestNeeded()) + down_prot.down(new Event(Event.OPEN_BARRIER)); + } + } + + void handleStateRsp(final StateHeader hdr) { + Digest tmp_digest = hdr.my_digest; + if (isDigestNeeded()) { + if (tmp_digest == null) { + if (log.isWarnEnabled()) + log.warn("digest received from " + hdr.sender + + " is null, skipping setting digest !"); + } else { + down_prot.down(new Event(Event.SET_DIGEST, tmp_digest)); + } + } + if (use_default_transport) { + // have to use another thread to read state while state recipient + // has to accept state messages from state provider + Thread t = getThreadFactory().newThread(new Runnable() { + public void run() { + openAndProvideInputStreamToStateReceiver(hdr.sender, hdr.getStateId()); + } + }, "STREAMING_STATE_TRANSFER state reader"); + t.start(); + } else { + connectToStateProvider(hdr); + } + } + + private void openAndProvideInputStreamToStateReceiver(Address stateProvider, String state_id) { + BufferedInputStream bis = null; + try { + bis = new BufferedInputStream(new StateInputStream(), socket_buffer_size); + up_prot.up(new Event(Event.STATE_TRANSFER_INPUTSTREAM, new StateTransferInfo( + stateProvider, bis, state_id))); + } catch (IOException e) { + // pass null stream up so that JChannel.getState() returns false + log.error("Could not provide state recipient with appropriate stream", e); + InputStream is = null; + up_prot.up(new Event(Event.STATE_TRANSFER_INPUTSTREAM, new StateTransferInfo( + stateProvider, is, state_id))); + } finally { + Util.close(bis); + } + } + + private void openAndProvideOutputStreamToStateProvider(Address stateRequester, String state_id) { + BufferedOutputStream bos = null; + try { + bos = new BufferedOutputStream(new StateOutputStream(stateRequester, state_id),socket_buffer_size); + up_prot.up(new Event(Event.STATE_TRANSFER_OUTPUTSTREAM, new StateTransferInfo( + stateRequester, bos, state_id))); + } catch (IOException e) { + if (log.isWarnEnabled()) { + log.warn("StateOutputStream could not be given to application", e); + } + } finally { + Util.close(bos); + } + } + + private void connectToStateProvider(StateHeader hdr) { + IpAddress address = hdr.bind_addr; + String tmp_state_id = hdr.getStateId(); + StreamingInputStreamWrapper wrapper = null; + StateTransferInfo sti = null; + Socket socket = new Socket(); + try { + socket.bind(new InetSocketAddress(bind_addr, 0)); + int bufferSize = socket.getReceiveBufferSize(); + socket.setReceiveBufferSize(socket_buffer_size); + if (log.isDebugEnabled()) + log.debug("Connecting to state provider " + address.getIpAddress() + ":" + + address.getPort() + ", original buffer size was " + bufferSize + + " and was reset to " + socket.getReceiveBufferSize()); + socket.connect(new InetSocketAddress(address.getIpAddress(), address.getPort())); + if (log.isDebugEnabled()) + log.debug("Connected to state provider, my end of the socket is " + + socket.getLocalAddress() + ":" + socket.getLocalPort() + + " passing inputstream up..."); + + // write out our state_id and address + ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream()); + out.writeObject(tmp_state_id); + out.writeObject(local_addr); + + wrapper = new StreamingInputStreamWrapper(socket); + sti = new StateTransferInfo(hdr.sender, wrapper, tmp_state_id); + up_prot.up(new Event(Event.STATE_TRANSFER_INPUTSTREAM, sti)); + } catch (IOException e) { + if (log.isWarnEnabled()) { + log.warn("State reader socket thread spawned abnormaly", e); + } + + // pass null stream up so that JChannel.getState() returns false + InputStream is = null; + sti = new StateTransferInfo(hdr.sender, is, tmp_state_id); + up_prot.up(new Event(Event.STATE_TRANSFER_INPUTSTREAM, sti)); + } finally { + if (!socket.isConnected()) { + if (log.isWarnEnabled()) + log.warn("Could not connect to state provider. Closing socket..."); + } + Util.close(wrapper); + Util.close(socket); + } + } + + /* + * ------------------------ End of Private Methods -------------------------------------------- + */ + + private class StateProviderThreadSpawner implements Runnable { + private final ExecutorService pool; + + private final ServerSocket serverSocket; + + private final IpAddress address; + + Thread runner; + + private volatile boolean running = true; + + public StateProviderThreadSpawner(ExecutorService pool, ServerSocket stateServingSocket) { + super(); + this.pool = pool; + this.serverSocket = stateServingSocket; + this.address = new IpAddress(STREAMING_STATE_TRANSFER.this.bind_addr, serverSocket + .getLocalPort()); + } + + public void run() { + runner = Thread.currentThread(); + for (; running;) { + try { + if (log.isDebugEnabled()) + log.debug("StateProviderThreadSpawner listening at " + + getServerSocketAddress() + "..."); + + final Socket socket = serverSocket.accept(); + pool.execute(new Runnable() { + public void run() { + if (log.isDebugEnabled()) + log.debug("Accepted request for state transfer from " + + socket.getInetAddress() + ":" + socket.getPort() + + " handing of to PooledExecutor thread"); + new StateProviderHandler().process(socket); + } + }); + + } catch (IOException e) { + if (log.isWarnEnabled()) { + // we get this exception when we close server socket + // exclude that case + if (serverSocket != null && !serverSocket.isClosed()) { + log.warn("Spawning socket from server socket finished abnormaly", e); + } + } + } + } + } + + public IpAddress getServerSocketAddress() { + return address; + } + + public void stop() { + running = false; + try { + serverSocket.close(); + } catch (Exception ignored) { + } finally { + if (log.isDebugEnabled()) + log.debug("Waiting for StateProviderThreadSpawner to die ... "); + + if (runner != null) { + try { + runner.join(Global.THREAD_SHUTDOWN_WAIT_TIME); + } catch (InterruptedException ignored) { + Thread.currentThread().interrupt(); + } + } + + if (log.isDebugEnabled()) + log.debug("Shutting the thread pool down... "); + + pool.shutdownNow(); + try { + pool.awaitTermination(Global.THREADPOOL_SHUTDOWN_WAIT_TIME, + TimeUnit.MILLISECONDS); + } catch (InterruptedException ignored) { + Thread.currentThread().interrupt(); + } + } + if (log.isDebugEnabled()) + log.debug("Thread pool is shutdown. All pool threads are cleaned up."); + } + } + + private class StateProviderHandler { + public void process(Socket socket) { + StreamingOutputStreamWrapper wrapper = null; + ObjectInputStream ois = null; + try { + int bufferSize = socket.getSendBufferSize(); + socket.setSendBufferSize(socket_buffer_size); + if (log.isDebugEnabled()) + log.debug("Running on " + Thread.currentThread() + + ". Accepted request for state transfer from " + + socket.getInetAddress() + ":" + socket.getPort() + + ", original buffer size was " + bufferSize + " and was reset to " + + socket.getSendBufferSize() + ", passing outputstream up... "); + + ois = new ObjectInputStream(socket.getInputStream()); + String state_id = (String) ois.readObject(); + Address stateRequester = (Address) ois.readObject(); + wrapper = new StreamingOutputStreamWrapper(socket); + StateTransferInfo sti = new StateTransferInfo(stateRequester, wrapper, state_id); + up_prot.up(new Event(Event.STATE_TRANSFER_OUTPUTSTREAM, sti)); + } catch (IOException e) { + if (log.isWarnEnabled()) { + log.warn("State writer socket thread spawned abnormaly", e); + } + } catch (ClassNotFoundException e) { + // thrown by ois.readObject() + // should never happen since String/Address are core classes + } finally { + if (!socket.isConnected()) { + if (log.isWarnEnabled()) + log.warn("Could not receive connection from state receiver. Closing socket..."); + } + Util.close(wrapper); + Util.close(socket); + } + } + } + + private class StreamingInputStreamWrapper extends InputStream { + + private final InputStream delegate; + + private final Socket inputStreamOwner; + + private final AtomicBoolean closed = new AtomicBoolean(false); + + public StreamingInputStreamWrapper(Socket inputStreamOwner) throws IOException { + super(); + this.inputStreamOwner = inputStreamOwner; + this.delegate = new BufferedInputStream(inputStreamOwner.getInputStream()); + } + + public int available() throws IOException { + return delegate.available(); + } + + public void close() throws IOException { + if (closed.compareAndSet(false, true)) { + if (log.isDebugEnabled()) { + log.debug("State reader is closing the socket "); + } + Util.close(delegate); + Util.close(inputStreamOwner); + up_prot.up(new Event(Event.STATE_TRANSFER_INPUTSTREAM_CLOSED)); + down(new Event(Event.STATE_TRANSFER_INPUTSTREAM_CLOSED)); + } + } + + public synchronized void mark(int readlimit) { + delegate.mark(readlimit); + } + + public boolean markSupported() { + return delegate.markSupported(); + } + + public int read() throws IOException { + return delegate.read(); + } + + public int read(byte[] b, int off, int len) throws IOException { + return delegate.read(b, off, len); + } + + public int read(byte[] b) throws IOException { + return delegate.read(b); + } + + public synchronized void reset() throws IOException { + delegate.reset(); + } + + public long skip(long n) throws IOException { + return delegate.skip(n); + } + } + + private class StreamingOutputStreamWrapper extends OutputStream { + private final Socket outputStreamOwner; + + private final OutputStream delegate; + + private final AtomicBoolean closed = new AtomicBoolean(false); + + private long bytesWrittenCounter = 0; + + public StreamingOutputStreamWrapper(Socket outputStreamOwner) throws IOException { + super(); + this.outputStreamOwner = outputStreamOwner; + this.delegate = new BufferedOutputStream(outputStreamOwner.getOutputStream()); + } + + public void close() throws IOException { + if (closed.compareAndSet(false, true)) { + if (log.isDebugEnabled()) { + log.debug("State writer is closing the socket "); + } + + Util.close(delegate); + Util.close(outputStreamOwner); + up_prot.up(new Event(Event.STATE_TRANSFER_OUTPUTSTREAM_CLOSED)); + down(new Event(Event.STATE_TRANSFER_OUTPUTSTREAM_CLOSED)); + + if (stats) { + avg_state_size = num_bytes_sent.addAndGet(bytesWrittenCounter) + / num_state_reqs.doubleValue(); + } + } + } + + public void flush() throws IOException { + delegate.flush(); + } + + public void write(byte[] b, int off, int len) throws IOException { + delegate.write(b, off, len); + bytesWrittenCounter += len; + } + + public void write(byte[] b) throws IOException { + delegate.write(b); + if (b != null) { + bytesWrittenCounter += b.length; + } + } + + public void write(int b) throws IOException { + delegate.write(b); + bytesWrittenCounter += 1; + } + } + + private class StateInputStream extends InputStream { + + private final AtomicBoolean closed; + + public StateInputStream() throws IOException { + super(); + this.closed = new AtomicBoolean(false); + } + + public void close() throws IOException { + if (closed.compareAndSet(false, true)) { + if (log.isDebugEnabled()) { + log.debug("State reader is closing the stream"); + } + stateQueue.clear(); + up(new Event(Event.STATE_TRANSFER_INPUTSTREAM_CLOSED)); + down(new Event(Event.STATE_TRANSFER_INPUTSTREAM_CLOSED)); + super.close(); + } + } + + public int read() throws IOException { + if (closed.get()) + return -1; + final byte[] array = new byte[1]; + return read(array) == -1 ? -1 : array[0]; + } + + public int read(byte[] b, int off, int len) throws IOException { + if (closed.get()) + return -1; + Message m = null; + try { + m = stateQueue.take(); + StateHeader hdr = (StateHeader) m.getHeader(getName()); + if (hdr.type == StateHeader.STATE_PART) { + return readAndTransferPayload(m, b, off, len); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new InterruptedIOException(); + } + return -1; + } + + private int readAndTransferPayload(Message m, byte[] b, int off, int len) { + byte[] buffer = m.getBuffer(); + if (log.isDebugEnabled()) { + log.debug(local_addr + " reading chunk of state " + "byte[] b=" + b.length + + ", off=" + off + ", buffer.length=" + buffer.length); + } + System.arraycopy(buffer, 0, b, off, buffer.length); + return buffer.length; + } + + public int read(byte[] b) throws IOException { + if (closed.get()) + return -1; + return read(b, 0, b.length); + } + } + + private class StateOutputStream extends OutputStream { + + private final Address stateRequester; + private final String state_id; + private final AtomicBoolean closed; + private long bytesWrittenCounter = 0; + + public StateOutputStream(Address stateRequester, String state_id) throws IOException { + super(); + this.stateRequester = stateRequester; + this.state_id = state_id; + this.closed = new AtomicBoolean(false); + } + + public void close() throws IOException { + if (closed.compareAndSet(false, true)) { + if (log.isDebugEnabled()) { + log.debug("State writer " + local_addr + + " is closing the output stream for state_id " + state_id); + } + up(new Event(Event.STATE_TRANSFER_OUTPUTSTREAM_CLOSED)); + down(new Event(Event.STATE_TRANSFER_OUTPUTSTREAM_CLOSED)); + if (stats) { + avg_state_size = num_bytes_sent.addAndGet(bytesWrittenCounter) + / num_state_reqs.doubleValue(); + } + super.close(); + } + } + + public void write(byte[] b, int off, int len) throws IOException { + if (closed.get()) + throw closed(); + sendMessage(b, off, len); + } + + public void write(byte[] b) throws IOException { + if (closed.get()) + throw closed(); + sendMessage(b, 0, b.length); + } + + public void write(int b) throws IOException { + if (closed.get()) + throw closed(); + byte buf[] = new byte[]{(byte) b}; + write(buf); + } + + private void sendMessage(byte[] b, int off, int len) throws IOException { + Message m = new Message(stateRequester); + m.putHeader(getName(), new StateHeader(StateHeader.STATE_PART, local_addr, state_id)); + m.setBuffer(b, off, len); + bytesWrittenCounter += (len - off); + if (Thread.interrupted()) { + throw interrupted((int) bytesWrittenCounter); + } + down_prot.down(new Event(Event.MSG, m)); + if (log.isDebugEnabled()) { + log.debug(local_addr + " sent chunk of state to " + stateRequester + "byte[] b=" + + b.length + ", off=" + off + ", len=" + len); + } + } + + private IOException closed() { + return new IOException("The output stream is closed"); + } + + private InterruptedIOException interrupted(int cnt) { + final InterruptedIOException ex = new InterruptedIOException(); + ex.bytesTransferred = cnt; + return ex; + } + } + + public static class StateHeader extends Header implements Streamable { + public static final byte STATE_REQ = 1; + + public static final byte STATE_RSP = 2; + + public static final byte STATE_PART = 3; + + long id = 0; // state transfer ID (to separate multiple state transfers + // at the same time) + + byte type = 0; + + Address sender; // sender of state STATE_REQ or STATE_RSP + + Digest my_digest = null; // digest of sender (if type is STATE_RSP) + + IpAddress bind_addr = null; + + String state_id = null; // for partial state transfer + + public StateHeader() { + } // for externalization + + public StateHeader(byte type, Address sender, String state_id) { + this.type = type; + this.sender = sender; + this.state_id = state_id; + } + + public StateHeader(byte type, Address sender, long id, Digest digest) { + this.type = type; + this.sender = sender; + this.id = id; + this.my_digest = digest; + } + + public StateHeader(byte type, Address sender, IpAddress bind_addr, Digest digest, + String state_id) { + this.type = type; + this.sender = sender; + this.my_digest = digest; + this.bind_addr = bind_addr; + this.state_id = state_id; + } + + public int getType() { + return type; + } + + public Digest getDigest() { + return my_digest; + } + + public String getStateId() { + return state_id; + } + + public boolean equals(Object o) { + StateHeader other; + + if (sender != null && o != null) { + if (!(o instanceof StateHeader)) + return false; + other = (StateHeader) o; + return sender.equals(other.sender) && id == other.id; + } + return false; + } + + public int hashCode() { + if (sender != null) + return sender.hashCode() + (int) id; + else + return (int) id; + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("type=").append(type2Str(type)); + if (sender != null) + sb.append(", sender=").append(sender).append(" id=").append(id); + if (my_digest != null) + sb.append(", digest=").append(my_digest); + return sb.toString(); + } + + static String type2Str(int t) { + switch (t) { + case STATE_REQ : + return "STATE_REQ"; + case STATE_RSP : + return "STATE_RSP"; + case STATE_PART : + return "STATE_PART"; + default : + return ""; + } + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeObject(sender); + out.writeLong(id); + out.writeByte(type); + out.writeObject(my_digest); + out.writeObject(bind_addr); + out.writeUTF(state_id); + } + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + sender = (Address) in.readObject(); + id = in.readLong(); + type = in.readByte(); + my_digest = (Digest) in.readObject(); + bind_addr = (IpAddress) in.readObject(); + state_id = in.readUTF(); + } + + public void writeTo(DataOutputStream out) throws IOException { + out.writeByte(type); + out.writeLong(id); + Util.writeAddress(sender, out); + Util.writeStreamable(my_digest, out); + Util.writeStreamable(bind_addr, out); + Util.writeString(state_id, out); + } + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, + InstantiationException { + type = in.readByte(); + id = in.readLong(); + sender = Util.readAddress(in); + my_digest = (Digest) Util.readStreamable(Digest.class, in); + bind_addr = (IpAddress) Util.readStreamable(IpAddress.class, in); + state_id = Util.readString(in); + } + + public int size() { + int retval = Global.LONG_SIZE + Global.BYTE_SIZE; // id and type + + retval += Util.size(sender); + + retval += Global.BYTE_SIZE; // presence byte for my_digest + if (my_digest != null) + retval += my_digest.serializedSize(); + + retval += Util.size(bind_addr); + + retval += Global.BYTE_SIZE; // presence byte for state_id + if (state_id != null) + retval += state_id.length() + 2; + return retval; + } + } +} Index: 3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/package.html =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/package.html,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/protocols/pbcast/package.html 17 Aug 2012 14:51:23 -0000 1.1 @@ -0,0 +1,5 @@ + + + Supports probabilistic broadcasts. + + \ No newline at end of file Index: 3rdParty_sources/jgroups/org/jgroups/stack/AckMcastReceiverWindow.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/stack/AckMcastReceiverWindow.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/stack/AckMcastReceiverWindow.java 17 Aug 2012 14:51:27 -0000 1.1 @@ -0,0 +1,163 @@ +// $Id: AckMcastReceiverWindow.java,v 1.1 2012/08/17 14:51:27 marcin Exp $ + +package org.jgroups.stack; + + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.Address; + +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; +import java.net.UnknownHostException; + + +/** + * Keeps track of messages received from various senders. Acks each message received and checks whether + * it was already delivered. If yes, the message is discarded, otherwise it is delivered (passed up). + * The messages contain sequence numbers of old messages to be deleted, those are removed from the + * message table. + * + * @author Bela Ban June 17 1999 + */ +public class AckMcastReceiverWindow { + final Hashtable msgs=new Hashtable(); // sender -- Vector (of seqnos) + + protected static final Log log=LogFactory.getLog(AckMcastReceiverWindow.class); + + + + /** + Records the sender/seqno pair in the message table + @param sender The sender of the message + @param seqno The sequence number associated with the message + @return boolean If false, message is already present. Otherwise true. + */ + public boolean add(Object sender, long seqno) { + Vector seqnos=(Vector)msgs.get(sender); + Long val=new Long(seqno); + + if(seqnos == null) { + seqnos=new Vector(); + seqnos.addElement(val); + msgs.put(sender, seqnos); + return true; + } + + if(seqnos.contains(val)) + return false; + + seqnos.addElement(val); + return true; + } + + + + + public void remove(Object sender, Vector seqnos) { + Vector v=(Vector)msgs.get(sender); + Long seqno; + + if(v != null && seqnos != null) { + for(int i=0; i < seqnos.size(); i++) { + seqno=(Long)seqnos.elementAt(i); + v.removeElement(seqno); + } + } + } + + + + public long size() { + long ret=0; + + for(Enumeration e=msgs.elements(); e.hasMoreElements();) { + ret+=((Vector)e.nextElement()).size(); + } + + return ret; + } + + + public void reset() { + removeAll(); + } + + public void removeAll() {msgs.clear();} + + + public void suspect(Object sender) { + + if(log.isInfoEnabled()) log.info("suspect is " + sender); + msgs.remove(sender); + } + + + + public String toString() { + StringBuilder ret=new StringBuilder(); + Object sender; + + for(Enumeration e=msgs.keys(); e.hasMoreElements();) { + sender=e.nextElement(); + ret.append(sender).append(" --> ").append(msgs.get(sender)).append('\n'); + } + return ret.toString(); + } + + + + + + + public static void main(String[] args) throws UnknownHostException { + AckMcastReceiverWindow win=new AckMcastReceiverWindow(); + Address sender1=new IpAddress("janet", 1111); + Address sender2=new IpAddress("janet", 4444); + Address sender3=new IpAddress("janet", 6767); + Address sender4=new IpAddress("janet", 3333); + + win.add(sender1, 1); + win.add(sender1, 2); + + win.add(sender3, 2); + win.add(sender2, 2); + win.add(sender4, 2); + win.add(sender1, 3); + win.add(sender1, 2); + + + System.out.println(win); + + win.suspect(sender1); + System.out.println(win); + + win.add(sender1, 1); + win.add(sender1, 2); + win.add(sender1, 3); + win.add(sender1, 4); + win.add(sender1, 5); + win.add(sender1, 6); + win.add(sender1, 7); + win.add(sender1, 8); + + System.out.println(win); + + + Vector seqnos=new Vector(); + + seqnos.addElement(new Long(4)); + seqnos.addElement(new Long(6)); + seqnos.addElement(new Long(8)); + + win.remove(sender2, seqnos); + System.out.println(win); + + win.remove(sender1, seqnos); + System.out.println(win); + + + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/stack/AckMcastSenderWindow.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/stack/AckMcastSenderWindow.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/stack/AckMcastSenderWindow.java 17 Aug 2012 14:51:27 -0000 1.1 @@ -0,0 +1,601 @@ +package org.jgroups.stack; + + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.Address; +import org.jgroups.Message; +import org.jgroups.util.TimeScheduler; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.*; +import java.util.concurrent.Future; + + +/** + * Keeps track of ACKs from receivers for each message. When a new message is + * sent, it is tagged with a sequence number and the receiver set (set of + * members to which the message is sent) and added to a hashtable + * (key = sequence number, val = message + receiver set). Each incoming ACK + * is noted and when all ACKs for a specific sequence number haven been + * received, the corresponding entry is removed from the hashtable. A + * retransmission thread periodically re-sends the message point-to-point to + * all receivers from which no ACKs have been received yet. A view change or + * suspect message causes the corresponding non-existing receivers to be + * removed from the hashtable. + *

        + * This class may need flow control in order to avoid needless + * retransmissions because of timeouts. + * @author Bela Ban June 9 1999, 2007 + * @author John Georgiadis May 8 2001 + * @version $Id: AckMcastSenderWindow.java,v 1.1 2012/08/17 14:51:27 marcin Exp $ + */ +public class AckMcastSenderWindow { + /** + * Called by retransmitter thread whenever a message needs to be re-sent + * to a destination. dest has to be set in the + * dst field of msg, as the latter was sent + * multicast, but now we are sending a unicast message. Message has to be + * copied before sending it (as headers will be appended and therefore + * the message changed!). + */ + public interface RetransmitCommand { + /** + * Retranmit the given msg + * @param seqno the sequence number associated with the message + * @param msg the msg to retransmit (it should be a copy!) + * @param dest the msg destination + */ + void retransmit(long seqno, Message msg, Address dest); + } + + + + + + private static final long SEC=1000; + /** + * Default retransmit intervals (ms) - exponential approx. + */ + private static final Interval RETRANSMIT_TIMEOUTS=new StaticInterval(2 * SEC, 3 * SEC, 5 * SEC, 8 * SEC); + /** + * Default retransmit thread suspend timeout (ms) + */ + + protected static final Log log=LogFactory.getLog(AckMcastSenderWindow.class); + + + // Msg tables related + /** + * Table of pending msgs: seqno -> Entry + */ + private final Map msgs=new HashMap(); + + /** + * List of recently suspected members. Used to cease retransmission to suspected members + */ + private final LinkedList suspects=new LinkedList(); + + /** + * Max number in suspects list + */ + private static final int max_suspects=20; + + /** + * List of acknowledged msgs since the last call to + * getStableMessages() + */ + private final List stable_msgs=new LinkedList(); + /** + * Whether a call to waitUntilAcksReceived() is still active + */ + private boolean waiting=false; + + // Retransmission thread related + /** + * Whether retransmitter is externally provided or owned by this object + */ + private boolean retransmitter_owned; + /** + * The retransmission scheduler + */ + private TimeScheduler timer=null; + /** + * Retransmission intervals + */ + private Interval retransmit_intervals; + /** + * The callback object for retransmission + */ + private RetransmitCommand cmd=null; + + + /** + * Convert exception stack trace to string + */ + private static String _toString(Throwable ex) { + StringWriter sw=new StringWriter(); + PrintWriter pw=new PrintWriter(sw); + ex.printStackTrace(pw); + return (sw.toString()); + } + + + /** + * @param entry the record associated with the msg to retransmit. It + * contains the list of receivers that haven't yet ack reception + */ + private void _retransmit(Entry entry) { + Address sender; + boolean received; + + synchronized(entry) { + for(Enumeration e=entry.senders.keys(); e.hasMoreElements();) { + sender=(Address)e.nextElement(); + received=((Boolean)entry.senders.get(sender)).booleanValue(); + if(!received) { + if(suspects.contains(sender)) { + + if(log.isWarnEnabled()) log.warn("removing " + sender + + " from retransmit list as it is in the suspect list"); + remove(sender); + continue; + } + + if(log.isInfoEnabled()) log.info("--> retransmitting msg #" + + entry.seqno + " to " + sender); + cmd.retransmit(entry.seqno, entry.msg.copy(), sender); + } + } + } + } + + + /** + * Setup this object's state + * @param cmd the callback object for retranmissions + * @param retransmit_intervals the interval between two consecutive + * retransmission attempts + * @param timer the external scheduler to use to schedule retransmissions + * @param sched_owned if true, the scheduler is owned by this object and + * can be started/stopped/destroyed. If false, the scheduler is shared + * among multiple objects and start()/stop() should not be called from + * within this object + * @throws IllegalArgumentException if cmd is null + */ + private void init(RetransmitCommand cmd, Interval retransmit_intervals, TimeScheduler timer, boolean sched_owned) { + if(cmd == null) { + if(log.isErrorEnabled()) log.error("command is null. Cannot retransmit " + "messages !"); + throw new IllegalArgumentException("cmd"); + } + + retransmitter_owned=sched_owned; + this.timer=timer; + this.retransmit_intervals=retransmit_intervals; + this.cmd=cmd; + } + + + /** + * Create and start the retransmitter + * @param cmd the callback object for retranmissions + * @param retransmit_intervals the interval between two consecutive + * retransmission attempts + * @param sched the external scheduler to use to schedule retransmissions + * @throws IllegalArgumentException if cmd is null + */ + public AckMcastSenderWindow(RetransmitCommand cmd, + Interval retransmit_intervals, TimeScheduler sched) { + init(cmd, retransmit_intervals, sched, false); + } + + + /** + * Create and start the retransmitter + * @param cmd the callback object for retranmissions + * @param sched the external scheduler to use to schedule retransmissions + * @throws IllegalArgumentException if cmd is null + */ + public AckMcastSenderWindow(RetransmitCommand cmd, TimeScheduler sched) { + init(cmd, RETRANSMIT_TIMEOUTS, sched, false); + } + + + /** + * Create and start the retransmitter + * @param cmd the callback object for retranmissions + * @param retransmit_intervals the interval between two consecutive + * retransmission attempts + * @throws IllegalArgumentException if cmd is null + */ + public AckMcastSenderWindow(RetransmitCommand cmd, Interval retransmit_intervals) { + init(cmd, retransmit_intervals, new TimeScheduler(), true); + } + + /** + * Create and start the retransmitter + * @param cmd the callback object for retranmissions + * @throws IllegalArgumentException if cmd is null + */ + public AckMcastSenderWindow(RetransmitCommand cmd) { + this(cmd, RETRANSMIT_TIMEOUTS); + } + + + /** + * Adds a new message to the hash table. + * @param seqno The sequence number associated with the message + * @param msg The message (should be a copy!) + * @param receivers The set of addresses to which the message was sent + * and from which consequently an ACK is expected + */ + public void add(long seqno, Message msg, Vector receivers) { + if(waiting) return; + if(receivers.isEmpty()) return; + + synchronized(msgs) { + if(msgs.get(new Long(seqno)) != null) return; + // each entry needs its own retransmission interval, intervals are stateful *and* mutable, so we *need* to copy ! + Entry e=new Entry(seqno, msg, receivers, retransmit_intervals.copy()); + Future future=timer.scheduleWithDynamicInterval(e); + e.setFuture(future); + msgs.put(new Long(seqno), e); + } + } + + + /** + * An ACK has been received from sender. Tag the sender in + * the hash table as 'received'. If all ACKs have been received, remove + * the entry all together. + * @param seqno The sequence number of the message for which an ACK has + * been received. + * @param sender The sender which sent the ACK + */ + public void ack(long seqno, Address sender) { + Entry entry; + Boolean received; + + synchronized(msgs) { + entry=msgs.get(new Long(seqno)); + if(entry == null) return; + + synchronized(entry) { + received=(Boolean)entry.senders.get(sender); + if(received == null || received.booleanValue()) return; + + // If not yet received + entry.senders.put(sender, Boolean.TRUE); + entry.num_received++; + if(!entry.allReceived()) return; + } + + synchronized(stable_msgs) { + entry.cancel(); + msgs.remove(new Long(seqno)); + stable_msgs.add(new Long(seqno)); + } + // wake up waitUntilAllAcksReceived() method + msgs.notifyAll(); + } + } + + + /** + * Remove obj from all receiver sets and wake up + * retransmission thread. + * @param obj the sender to remove + */ + public void remove(Address obj) { + Long key; + Entry entry; + + synchronized(msgs) { + for(Iterator it=msgs.keySet().iterator(); it.hasNext();) { + key=it.next(); + entry=msgs.get(key); + synchronized(entry) { + //if (((Boolean)entry.senders.remove(obj)).booleanValue()) entry.num_received--; + //if (!entry.allReceived()) continue; + Boolean received=(Boolean)entry.senders.remove(obj); + if(received == null) continue; // suspected member not in entry.senders ? + if(received.booleanValue()) entry.num_received--; + if(!entry.allReceived()) continue; + } + synchronized(stable_msgs) { + entry.cancel(); + msgs.remove(key); + stable_msgs.add(key); + } + // wake up waitUntilAllAcksReceived() method + msgs.notifyAll(); + } + } + } + + + /** + * Process with address suspected is suspected: remove it + * from all receiver sets. This means that no ACKs are expected from this + * process anymore. + * @param suspected The suspected process + */ + public void suspect(Address suspected) { + + if(log.isInfoEnabled()) log.info("suspect is " + suspected); + remove(suspected); + suspects.add(suspected); + if(suspects.size() >= max_suspects) + suspects.removeFirst(); + } + + + /** + * @return a copy of stable messages, or null (if non available). Removes + * all stable messages afterwards + */ + public List getStableMessages() { + List retval; + + synchronized(stable_msgs) { + retval=(!stable_msgs.isEmpty())? new LinkedList(stable_msgs) : null; + if(!stable_msgs.isEmpty()) stable_msgs.clear(); + } + + return retval; + } + + + public void clearStableMessages() { + synchronized(stable_msgs) { + stable_msgs.clear(); + } + } + + + /** + * @return the number of currently pending msgs + */ + public long size() { + synchronized(msgs) { + return (msgs.size()); + } + } + + + /** + * Returns the number of members for a given entry for which acks have to be received + */ + public long getNumberOfResponsesExpected(long seqno) { + Entry entry=msgs.get(new Long(seqno)); + if(entry != null) + return entry.senders.size(); + else + return -1; + } + + /** + * Returns the number of members for a given entry for which acks have been received + */ + public long getNumberOfResponsesReceived(long seqno) { + Entry entry=msgs.get(new Long(seqno)); + if(entry != null) + return entry.num_received; + else + return -1; + } + + /** + * Prints all members plus whether an ack has been received from those members for a given seqno + */ + public String printDetails(long seqno) { + Entry entry=msgs.get(new Long(seqno)); + if(entry != null) + return entry.toString(); + else + return null; + } + + + /** + * Waits until all outstanding messages have been ACKed by all receivers. + * Takes into account suspicions and view changes. Returns when there are + * no entries left in the hashtable. While waiting, no entries can be + * added to the hashtable (they will be discarded). + * @param timeout Miliseconds to wait. 0 means wait indefinitely. + */ + public void waitUntilAllAcksReceived(long timeout) { + long time_to_wait, start_time, current_time; + Address suspect; + + // remove all suspected members from retransmission + for(Iterator it=suspects.iterator(); it.hasNext();) { + suspect=(Address)it.next(); + remove(suspect); + } + + time_to_wait=timeout; + waiting=true; + if(timeout <= 0) { + synchronized(msgs) { + while(!msgs.isEmpty()) { + try { + msgs.wait(); + } + catch(InterruptedException ex) { + } + } + } + } + else { + start_time=System.currentTimeMillis(); + synchronized(msgs) { + while(!msgs.isEmpty()) { + current_time=System.currentTimeMillis(); + time_to_wait=timeout - (current_time - start_time); + if(time_to_wait <= 0) break; + + try { + msgs.wait(time_to_wait); + } + catch(InterruptedException ex) { + if(log.isWarnEnabled()) log.warn(ex.toString()); + } + } + } + } + waiting=false; + } + + + + /** + * Stop the rentransmition and clear all pending msgs. + *

        + * If this retransmitter has been provided an externally managed + * scheduler, then just clear all msgs and the associated tasks, else + * stop the scheduler. In this case the method blocks until the + * scheduler's thread is dead. Only the owner of the scheduler should + * stop it. + */ + public void stop() { + + // i. If retransmitter is owned, stop it else cancel all tasks + // ii. Clear all pending msgs and notify anyone waiting + synchronized(msgs) { + if(retransmitter_owned) { + try { + timer.stop(); + } + catch(InterruptedException ex) { + if(log.isErrorEnabled()) log.error(_toString(ex)); + } + } + else { + for(Entry entry: msgs.values()) { + entry.cancel(); + } + } + msgs.clear(); + msgs.notifyAll(); // wake up waitUntilAllAcksReceived() method + } + } + + + /** + * Remove all pending msgs from the hashtable. Cancel all associated + * tasks in the retransmission scheduler + */ + public void reset() { + if(waiting) return; + synchronized(msgs) { + for(Entry entry: msgs.values()) { + entry.cancel(); + } + msgs.clear(); + msgs.notifyAll(); + } + } + + + public String toString() { + StringBuilder ret; + Entry entry; + + ret=new StringBuilder(); + synchronized(msgs) { + ret.append("msgs: (").append(msgs.size()).append(')'); + for(Long key: msgs.keySet()) { + entry=msgs.get(key); + ret.append("key = ").append(key).append(", value = ").append(entry).append('\n'); + } + synchronized(stable_msgs) { + ret.append("\nstable_msgs: ").append(stable_msgs); + } + } + + return(ret.toString()); + } + + + + /** + * The retransmit task executed by the scheduler in regular intervals + */ + private static abstract class Task implements TimeScheduler.Task { + private final Interval intervals; + private Future future; + + protected Task(Interval intervals) { + this.intervals=intervals; + } + + + public void setFuture(Future future) { + this.future=future; + } + + public long nextInterval() { + return intervals.next(); + } + + public void cancel() { + if(future != null) { + future.cancel(false); + future=null; + } + } + + } + + + /** + * The entry associated with a pending msg + */ + private class Entry extends Task { + /** + * The msg sequence number + */ + public final long seqno; + /** + * The msg to retransmit + */ + public Message msg=null; + /** + * destination addr -> boolean (true = received, false = not) + */ + public final Hashtable senders=new Hashtable(); + /** + * How many destinations have received the msg + */ + public int num_received=0; + + public Entry(long seqno, Message msg, Vector dests, Interval intervals) { + super(intervals); + this.seqno=seqno; + this.msg=msg; + for(int i=0; i < dests.size(); i++) + senders.put(dests.elementAt(i), Boolean.FALSE); + } + + boolean allReceived() { + return (num_received >= senders.size()); + } + + /** + * Retransmit this entry + */ + public void run() { + _retransmit(this); + } + + public String toString() { + StringBuilder buf=new StringBuilder(); + buf.append("num_received = ").append(num_received).append(", received msgs = ").append(senders); + return buf.toString(); + } + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/stack/AckReceiverWindow.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/stack/AckReceiverWindow.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/stack/AckReceiverWindow.java 17 Aug 2012 14:51:27 -0000 1.1 @@ -0,0 +1,141 @@ + +package org.jgroups.stack; + + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.Message; + +import java.util.HashMap; +import java.util.Map; +import java.util.TreeSet; +import java.util.concurrent.locks.ReentrantLock; + + +/** + * Counterpart of AckSenderWindow. Simple FIFO buffer. + * Every message received is ACK'ed (even duplicates) and added to a hashmap + * keyed by seqno. The next seqno to be received is stored in next_to_remove. When a message with + * a seqno less than next_to_remove is received, it will be discarded. The remove() method removes + * and returns a message whose seqno is equal to next_to_remove, or null if not found.
        + * Change May 28 2002 (bela): replaced TreeSet with HashMap. Keys do not need to be sorted, and adding a key to + * a sorted set incurs overhead. + * + * @author Bela Ban + * @version $Id: AckReceiverWindow.java,v 1.1 2012/08/17 14:51:27 marcin Exp $ + */ +public class AckReceiverWindow { + long next_to_remove=0; + final Map msgs=new HashMap(); // keys: seqnos (Long), values: Messages + static final Log log=LogFactory.getLog(AckReceiverWindow.class); + final ReentrantLock lock=new ReentrantLock(); + + public AckReceiverWindow(long initial_seqno) { + this.next_to_remove=initial_seqno; + } + + public ReentrantLock getLock() { + return lock; + } + + /** Adds a new message. Message cannot be null + * @return True if the message was added, false if not (e.g. duplicate, message was already present) + */ + public boolean add(long seqno, Message msg) { + if(msg == null) + throw new IllegalArgumentException("msg must be non-null"); + synchronized(msgs) { + if(seqno < next_to_remove) { + if(log.isTraceEnabled()) + log.trace("discarded msg with seqno=" + seqno + " (next msg to receive is " + next_to_remove + ')'); + return false; + } + if(!msgs.containsKey(seqno)) { + msgs.put(seqno, msg); + return true; + } + else { + if(log.isTraceEnabled()) + log.trace("seqno " + seqno + " already received - dropping it"); + return false; + } + } + } + + + /** + * Removes a message whose seqno is equal to next_to_remove, increments the latter. + * Returns message that was removed, or null, if no message can be removed. Messages are thus + * removed in order. + */ + public Message remove() { + Message retval; + + synchronized(msgs) { + retval=msgs.remove(next_to_remove); + if(retval != null) { + if(log.isTraceEnabled()) + log.trace("removed seqno=" + next_to_remove); + next_to_remove++; + } + } + return retval; + } + + public Message removeOOBMessage() { + Message retval; + + synchronized(msgs) { + retval=msgs.get(next_to_remove); + if(retval != null) { + if(!retval.isFlagSet(Message.OOB)) { + return null; + } + retval=msgs.remove(next_to_remove); + if(log.isTraceEnabled()) + log.trace("removed OOB message with seqno=" + next_to_remove); + next_to_remove++; + } + } + return retval; + } + + + public boolean hasMessagesToRemove() { + synchronized(msgs) { + return msgs.containsKey(next_to_remove); + } + } + + + public void reset() { + synchronized(msgs) { + msgs.clear(); + } + } + + public int size() { + return msgs.size(); + } + + public String toString() { + StringBuilder sb=new StringBuilder(); + sb.append(msgs.size()).append(" msgs (").append("next=").append(next_to_remove).append(")"); + TreeSet s=new TreeSet(msgs.keySet()); + if(!s.isEmpty()) { + sb.append(" [").append(s.first()).append(" - ").append(s.last()).append("]"); + sb.append(": ").append(s); + } + return sb.toString(); + } + + + public String printDetails() { + StringBuilder sb=new StringBuilder(); + sb.append(msgs.size()).append(" msgs (").append("next=").append(next_to_remove).append(")"). + append(", msgs=" ).append(new TreeSet(msgs.keySet())); + return sb.toString(); + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/stack/AckSenderWindow.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/stack/AckSenderWindow.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/stack/AckSenderWindow.java 17 Aug 2012 14:51:27 -0000 1.1 @@ -0,0 +1,292 @@ +// $Id: AckSenderWindow.java,v 1.1 2012/08/17 14:51:27 marcin Exp $ + +package org.jgroups.stack; + + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.Address; +import org.jgroups.Message; +import org.jgroups.util.TimeScheduler; +import org.jgroups.util.Util; + +import java.util.TreeSet; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + + +/** + * ACK-based sliding window for a sender. Messages are added to the window keyed by seqno + * When an ACK is received, the corresponding message is removed. The Retransmitter + * continously iterates over the entries in the hashmap, retransmitting messages based on their + * creation time and an (increasing) timeout. When there are no more messages in the retransmission + * table left, the thread terminates. It will be re-activated when a new entry is added to the + * retransmission table. + * @author Bela Ban + */ +public class AckSenderWindow implements Retransmitter.RetransmitCommand { + RetransmitCommand retransmit_command = null; // called to request XMIT of msg + final ConcurrentMap msgs=new ConcurrentHashMap(); // keys: seqnos (Long), values: Messages + Interval interval=new StaticInterval(400,800,1200,1600); + final Retransmitter retransmitter; + static final Log log=LogFactory.getLog(AckSenderWindow.class); + + + public interface RetransmitCommand { + void retransmit(long seqno, Message msg); + } + + + /** + * Creates a new instance. Thre retransmission thread has to be started separately with + * start(). + * @param com If not null, its method retransmit() will be called when a message + * needs to be retransmitted (called by the Retransmitter). + */ + public AckSenderWindow(RetransmitCommand com) { + retransmit_command = com; + retransmitter = new Retransmitter(null, this); + retransmitter.setRetransmitTimeouts(interval); + } + + + public AckSenderWindow(RetransmitCommand com, Interval interval) { + retransmit_command = com; + this.interval = interval; + retransmitter = new Retransmitter(null, this); + retransmitter.setRetransmitTimeouts(interval); + } + + + + public AckSenderWindow(RetransmitCommand com, Interval interval, TimeScheduler sched) { + retransmit_command = com; + this.interval = interval; + retransmitter = new Retransmitter(null, this, sched); + retransmitter.setRetransmitTimeouts(interval); + } + + public AckSenderWindow(RetransmitCommand com, Interval interval, TimeScheduler sched, Address sender) { + retransmit_command = com; + this.interval = interval; + retransmitter = new Retransmitter(sender, this, sched); + retransmitter.setRetransmitTimeouts(interval); + } + + + + public void reset() { + msgs.clear(); + + // moved out of sync scope: Retransmitter.reset()/add()/remove() are sync'ed anyway + // Bela Jan 15 2003 + retransmitter.reset(); + } + + + /** + * Adds a new message to the retransmission table. If the message won't have received an ack within + * a certain time frame, the retransmission thread will retransmit the message to the receiver. If + * a sliding window protocol is used, we only add up to window_size messages. If the table is + * full, we add all new messages to a queue. Those will only be added once the table drains below a certain + * threshold (min_threshold) + */ + public void add(long seqno, Message msg) { + msgs.putIfAbsent(seqno, msg); + retransmitter.add(seqno, seqno); + } + + + /** + * Removes the message from msgs, removing them also from retransmission. If + * sliding window protocol is used, and was queueing, check whether we can resume adding elements. + * Add all elements. If this goes above window_size, stop adding and back to queueing. Else + * set queueing to false. + */ + public void ack(long seqno) { + msgs.remove(new Long(seqno)); + retransmitter.remove(seqno); + } + + public int size() { + return msgs.size(); + } + + public String toString() { + StringBuilder sb=new StringBuilder(); + sb.append(msgs.size()).append(" msgs (").append(retransmitter.size()).append(" to retransmit): "); + TreeSet keys=new TreeSet(msgs.keySet()); + if(!keys.isEmpty()) + sb.append(keys.first()).append(" - ").append(keys.last()); + else + sb.append("[]"); + return sb.toString(); + } + + + public String printDetails() { + StringBuilder sb=new StringBuilder(); + sb.append(msgs.size()).append(" msgs (").append(retransmitter.size()).append(" to retransmit): "). + append(new TreeSet(msgs.keySet())); + return sb.toString(); + } + + /* -------------------------------- Retransmitter.RetransmitCommand interface ------------------- */ + public void retransmit(long first_seqno, long last_seqno, Address sender) { + Message msg; + + if(retransmit_command != null) { + if(log.isTraceEnabled()) + log.trace(new StringBuilder("retransmitting messages ").append(first_seqno). + append(" - ").append(last_seqno).append(" from ").append(sender)); + for(long i = first_seqno; i <= last_seqno; i++) { + if((msg=msgs.get(i)) != null) { // find the message to retransmit + retransmit_command.retransmit(i, msg); + } + } + } + } + /* ----------------------------- End of Retransmitter.RetransmitCommand interface ---------------- */ + + + + + + /* ---------------------------------- Private methods --------------------------------------- */ + + /* ------------------------------ End of Private methods ------------------------------------ */ + + + + + /** Struct used to store message alongside with its seqno in the message queue */ + static class Entry { + final long seqno; + final Message msg; + + Entry(long seqno, Message msg) { + this.seqno = seqno; + this.msg = msg; + } + } + + + static class Dummy implements RetransmitCommand { + static final long last_xmit_req = 0; + long curr_time; + + + public void retransmit(long seqno, Message msg) { + if(log.isDebugEnabled()) log.debug("seqno=" + seqno); + curr_time = System.currentTimeMillis(); + } + } + + + public static void main(String[] args) { + Interval xmit_timeouts=new StaticInterval(1000, 2000, 3000, 4000); + AckSenderWindow win=new AckSenderWindow(new Dummy(), xmit_timeouts); + + + + final int NUM = 1000; + + for (int i = 1; i < NUM; i++) + win.add(i, new Message()); + + + System.out.println(win); + Util.sleep(5000); + + for (int i = 1; i < NUM; i++) { + if (i % 2 == 0) // ack the even seqnos + win.ack(i); + } + + System.out.println(win); + Util.sleep(4000); + + for (int i = 1; i < NUM; i++) { + if (i % 2 != 0) // ack the odd seqnos + win.ack(i); + } + System.out.println(win); + + win.add(3, new Message()); + win.add(5, new Message()); + win.add(4, new Message()); + win.add(8, new Message()); + win.add(9, new Message()); + win.add(6, new Message()); + win.add(7, new Message()); + win.add(3, new Message()); + System.out.println(win); + + + try { + Thread.sleep(5000); + win.ack(5); + System.out.println("ack(5)"); + win.ack(4); + System.out.println("ack(4)"); + win.ack(6); + System.out.println("ack(6)"); + win.ack(7); + System.out.println("ack(7)"); + win.ack(8); + System.out.println("ack(8)"); + win.ack(6); + System.out.println("ack(6)"); + win.ack(9); + System.out.println("ack(9)"); + System.out.println(win); + + Thread.sleep(5000); + win.ack(3); + System.out.println("ack(3)"); + System.out.println(win); + + Thread.sleep(3000); + win.add(10, new Message()); + win.add(11, new Message()); + System.out.println(win); + Thread.sleep(3000); + win.ack(10); + System.out.println("ack(10)"); + win.ack(11); + System.out.println("ack(11)"); + System.out.println(win); + + win.add(12, new Message()); + win.add(13, new Message()); + win.add(14, new Message()); + win.add(15, new Message()); + win.add(16, new Message()); + System.out.println(win); + + Util.sleep(1000); + win.ack(12); + System.out.println("ack(12)"); + win.ack(13); + System.out.println("ack(13)"); + + win.ack(15); + System.out.println("ack(15)"); + System.out.println(win); + + Util.sleep(5000); + win.ack(16); + System.out.println("ack(16)"); + System.out.println(win); + + Util.sleep(1000); + + win.ack(14); + System.out.println("ack(14)"); + System.out.println(win); + } catch (Exception e) { + log.error(e); + } + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/stack/Configurator.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/stack/Configurator.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/stack/Configurator.java 17 Aug 2012 14:51:28 -0000 1.1 @@ -0,0 +1,862 @@ +package org.jgroups.stack; + + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.Event; +import org.jgroups.Global; +import org.jgroups.protocols.TP; +import org.jgroups.util.Tuple; +import org.jgroups.util.Util; + +import java.io.IOException; +import java.io.PushbackReader; +import java.io.Reader; +import java.io.StringReader; +import java.util.*; +import java.util.concurrent.ConcurrentMap; + + +/** + * The task if this class is to setup and configure the protocol stack. A string describing + * the desired setup, which is both the layering and the configuration of each layer, is + * given to the configurator which creates and configures the protocol stack and returns + * a reference to the top layer (Protocol).

        + * Future functionality will include the capability to dynamically modify the layering + * of the protocol stack and the properties of each layer. + * @author Bela Ban + * @version $Id: Configurator.java,v 1.1 2012/08/17 14:51:28 marcin Exp $ + */ +public class Configurator { + + protected static final Log log=LogFactory.getLog(Configurator.class); + + + /** + * The configuration string has a number of entries, separated by a ':' (colon). + * Each entry consists of the name of the protocol, followed by an optional configuration + * of that protocol. The configuration is enclosed in parentheses, and contains entries + * which are name/value pairs connected with an assignment sign (=) and separated by + * a semicolon. + *

        UDP(in_port=5555;out_port=4445):FRAG(frag_size=1024)

        + * The first entry defines the bottommost layer, the string is parsed + * left to right and the protocol stack constructed bottom up. Example: the string + * "UDP(in_port=5555):FRAG(frag_size=32000):DEBUG" results is the following stack:

        +     *
        +     *   -----------------------
        +     *  | DEBUG                 |
        +     *  |-----------------------|
        +     *  | FRAG frag_size=32000  |
        +     *  |-----------------------|
        +     *  | UDP in_port=32000     |
        +     *   -----------------------
        +     * 
        + */ + public static Protocol setupProtocolStack(String configuration, ProtocolStack st) throws Exception { + Protocol protocol_stack=null; + Vector protocol_configs; + Vector protocols; + + protocol_configs=parseConfigurations(configuration); + protocols=createProtocols(protocol_configs, st); + if(protocols == null) + return null; + protocol_stack=connectProtocols(protocols); + return protocol_stack; + } + + + public static void initProtocolStack(List protocols) throws Exception { + Collections.reverse(protocols); + for(Protocol prot: protocols) { + prot.init(); + } + } + + public static void startProtocolStack(List protocols, String cluster_name, final Map> singletons) throws Exception { + Protocol above_prot=null; + for(final Protocol prot: protocols) { + if(prot instanceof TP) { + String singleton_name=((TP)prot).getSingletonName(); + if(singleton_name != null && singleton_name.length() > 0 && cluster_name != null) { + TP transport=(TP)prot; + final Map up_prots=transport.getUpProtocols(); + synchronized(singletons) { + synchronized(up_prots) { + Set keys=up_prots.keySet(); + if(keys.contains(cluster_name)) + throw new IllegalStateException("cluster '" + cluster_name + "' is already connected to singleton " + + "transport: " + keys); + + for(Iterator> it=up_prots.entrySet().iterator(); it.hasNext();) { + Map.Entry entry=it.next(); + Protocol tmp=entry.getValue(); + if(tmp == above_prot) { + it.remove(); + } + } + + if(above_prot != null) { + TP.ProtocolAdapter ad=new TP.ProtocolAdapter(cluster_name, prot.getName(), above_prot, prot, + transport.getThreadNamingPattern(), + transport.getLocalAddress()); + ad.setProtocolStack(above_prot.getProtocolStack()); + above_prot.setDownProtocol(ad); + up_prots.put(cluster_name, ad); + } + } + Tuple val=singletons.get(singleton_name); + if(val == null) { + singletons.put(singleton_name, new Tuple(transport,(short)1)); + } + else { + short num_starts=val.getVal2(); + val.setVal2((short)(num_starts +1)); + if(num_starts >= 1) { + if(above_prot != null) + above_prot.up(new Event(Event.SET_LOCAL_ADDRESS, transport.getLocalAddress())); + continue; + } + else { + prot.start(); + above_prot=prot; + continue; + } + } + } + } + } + prot.start(); + above_prot=prot; + } + } + + public static void stopProtocolStack(List protocols, String cluster_name, final Map> singletons) { + for(final Protocol prot: protocols) { + if(prot instanceof TP) { + String singleton_name=((TP)prot).getSingletonName(); + if(singleton_name != null && singleton_name.length() > 0) { + TP transport=(TP)prot; + final Map up_prots=transport.getUpProtocols(); + + synchronized(up_prots) { + up_prots.remove(cluster_name); + } + + synchronized(singletons) { + Tuple val=singletons.get(singleton_name); + if(val != null) { + short num_starts=(short)Math.max(val.getVal2() -1, 0); + val.setVal2(num_starts); + if(num_starts > 0) { + continue; // don't call TP.stop() if we still have references to the transport + } + else + singletons.remove(singleton_name); + } + } + } + } + prot.stop(); + } + } + + + public static void destroyProtocolStack(List protocols) { + for(Protocol prot: protocols) { + prot.destroy(); + } + } + + + public static Protocol findProtocol(Protocol prot_stack, String name) { + String s; + Protocol curr_prot=prot_stack; + + while(true) { + s=curr_prot.getName(); + if(s == null) + continue; + if(s.equals(name)) + return curr_prot; + curr_prot=curr_prot.getDownProtocol(); + if(curr_prot == null) + break; + } + return null; + } + + + public static Protocol getBottommostProtocol(Protocol prot_stack) { + Protocol tmp=null, curr_prot=prot_stack; + + while(true) { + if((tmp=curr_prot.getDownProtocol()) == null) + break; + curr_prot=tmp; + } + return curr_prot; + } + + + /** + * Creates a new protocol given the protocol specification. Initializes the properties and starts the + * up and down handler threads. + * @param prot_spec The specification of the protocol. Same convention as for specifying a protocol stack. + * An exception will be thrown if the class cannot be created. Example: + *
        "VERIFY_SUSPECT(timeout=1500)"
        Note that no colons (:) have to be + * specified + * @param stack The protocol stack + * @return Protocol The newly created protocol + * @exception Exception Will be thrown when the new protocol cannot be created + */ + public static Protocol createProtocol(String prot_spec, ProtocolStack stack) throws Exception { + ProtocolConfiguration config; + Protocol prot; + + if(prot_spec == null) throw new Exception("Configurator.createProtocol(): prot_spec is null"); + + // parse the configuration for this protocol + config=new ProtocolConfiguration(prot_spec); + + // create an instance of the protocol class and configure it + prot=config.createLayer(stack); + prot.init(); + return prot; + } + + + /** + * Inserts an already created (and initialized) protocol into the protocol list. Sets the links + * to the protocols above and below correctly and adjusts the linked list of protocols accordingly. + * This should be done before starting the stack. + * @param prot The protocol to be inserted. Before insertion, a sanity check will ensure that none + * of the existing protocols have the same name as the new protocol. + * @param position Where to place the protocol with respect to the neighbor_prot (ABOVE, BELOW) + * @param neighbor_prot The name of the neighbor protocol. An exception will be thrown if this name + * is not found + * @param stack The protocol stack + * @exception Exception Will be thrown when the new protocol cannot be created, or inserted. + */ + public static void insertProtocol(Protocol prot, int position, String neighbor_prot, ProtocolStack stack) throws Exception { + if(neighbor_prot == null) throw new Exception("Configurator.insertProtocol(): neighbor_prot is null"); + if(position != ProtocolStack.ABOVE && position != ProtocolStack.BELOW) + throw new Exception("position has to be ABOVE or BELOW"); + + Protocol neighbor=stack.findProtocol(neighbor_prot); + if(neighbor == null) + throw new Exception("protocol \"" + neighbor_prot + "\" not found in " + stack.printProtocolSpec(false)); + + insertProtocol(prot, neighbor, position); + } + + + public static void insertProtocol(Protocol prot, int position, Class neighbor_prot, ProtocolStack stack) throws Exception { + if(neighbor_prot == null) throw new Exception("Configurator.insertProtocol(): neighbor_prot is null"); + if(position != ProtocolStack.ABOVE && position != ProtocolStack.BELOW) + throw new Exception("position has to be ABOVE or BELOW"); + + Protocol neighbor=stack.findProtocol(neighbor_prot); + if(neighbor == null) + throw new Exception("protocol \"" + neighbor_prot + "\" not found in " + stack.printProtocolSpec(false)); + + insertProtocol(prot, neighbor, position); + } + + + protected static void insertProtocol(Protocol prot, Protocol neighbor, int position) { + // connect to the protocol layer below and above + if(position == ProtocolStack.BELOW) { + prot.setUpProtocol(neighbor); + Protocol below=neighbor.getDownProtocol(); + prot.setDownProtocol(below); + if(below != null) + below.setUpProtocol(prot); + neighbor.setDownProtocol(prot); + } + else { // ABOVE is default + Protocol above=neighbor.getUpProtocol(); + prot.setUpProtocol(above); + if(above != null) + above.setDownProtocol(prot); + prot.setDownProtocol(neighbor); + neighbor.setUpProtocol(prot); + } + } + + + /** + * Removes a protocol from the stack. Stops the protocol and readjusts the linked lists of + * protocols. + * @param prot_name The name of the protocol. Since all protocol names in a stack have to be unique + * (otherwise the stack won't be created), the name refers to just 1 protocol. + * @exception Exception Thrown if the protocol cannot be stopped correctly. + */ + public static Protocol removeProtocol(Protocol top_prot, String prot_name) throws Exception { + if(prot_name == null) return null; + Protocol prot=findProtocol(top_prot, prot_name); + if(prot == null) return null; + Protocol above=prot.getUpProtocol(), below=prot.getDownProtocol(); + if(above != null) + above.setDownProtocol(below); + if(below != null) + below.setUpProtocol(above); + prot.setUpProtocol(null); + prot.setDownProtocol(null); + return prot; + } + + + + /* ------------------------------- Private Methods ------------------------------------- */ + + + /** + * Creates a protocol stack by iterating through the protocol list and connecting + * adjacent layers. The list starts with the topmost layer and has the bottommost + * layer at the tail. + * @param protocol_list List of Protocol elements (from top to bottom) + * @return Protocol stack + */ + private static Protocol connectProtocols(Vector protocol_list) { + Protocol current_layer=null, next_layer=null; + + for(int i=0; i < protocol_list.size(); i++) { + current_layer=(Protocol)protocol_list.elementAt(i); + if(i + 1 >= protocol_list.size()) + break; + next_layer=(Protocol)protocol_list.elementAt(i + 1); + next_layer.setDownProtocol(current_layer); + current_layer.setUpProtocol(next_layer); + + if(current_layer instanceof TP) { + String singleton_name= ((TP)current_layer).getSingletonName(); + if(singleton_name != null && singleton_name.length() > 0) { + ConcurrentMap up_prots=((TP)current_layer).getUpProtocols(); + String key; + synchronized(up_prots) { + while(true) { + key=Global.DUMMY + System.currentTimeMillis(); + if(up_prots.containsKey(key)) + continue; + up_prots.put(key, next_layer); + break; + } + } + current_layer.setUpProtocol(null); + } + } + } + return current_layer; + } + + + /** + * Get a string of the form "P1(config_str1):P2:P3(config_str3)" and return + * ProtocolConfigurations for it. That means, parse "P1(config_str1)", "P2" and + * "P3(config_str3)" + * @param config_str Configuration string + * @return Vector of strings + */ + public static Vector parseProtocols(String config_str) throws IOException { + Vector retval=new Vector(); + PushbackReader reader=new PushbackReader(new StringReader(config_str)); + int ch; + StringBuilder sb; + boolean running=true; + + while(running) { + String protocol_name=readWord(reader); + sb=new StringBuilder(); + sb.append(protocol_name); + + ch=read(reader); + if(ch == -1) { + retval.add(sb.toString()); + break; + } + + if(ch == ':') { // no attrs defined + retval.add(sb.toString()); + continue; + } + + if(ch == '(') { // more attrs defined + reader.unread(ch); + String attrs=readUntil(reader, ')'); + sb.append(attrs); + retval.add(sb.toString()); + } + else { + retval.add(sb.toString()); + } + + while(true) { + ch=read(reader); + if(ch == ':') { + break; + } + if(ch == -1) { + running=false; + break; + } + } + } + reader.close(); + + return retval; + } + + + private static int read(Reader reader) throws IOException { + int ch=-1; + while((ch=reader.read()) != -1) { + if(!Character.isWhitespace(ch)) + return ch; + } + return ch; + } + + /** + * Return a number of ProtocolConfigurations in a vector + * @param configuration protocol-stack configuration string + * @return Vector of ProtocolConfigurations + */ + public static Vector parseConfigurations(String configuration) throws Exception { + Vector retval=new Vector(); + Vector protocol_string=parseProtocols(configuration); + String component_string; + ProtocolConfiguration protocol_config; + + if(protocol_string == null) + return null; + for(int i=0; i < protocol_string.size(); i++) { + component_string=(String)protocol_string.elementAt(i); + protocol_config=new ProtocolConfiguration(component_string); + retval.addElement(protocol_config); + } + return retval; + } + + + + private static String readUntil(Reader reader, char c) throws IOException { + StringBuilder sb=new StringBuilder(); + int ch; + while((ch=read(reader)) != -1) { + sb.append((char)ch); + if(ch == c) + break; + } + return sb.toString(); + } + + private static String readWord(PushbackReader reader) throws IOException { + StringBuilder sb=new StringBuilder(); + int ch; + + while((ch=read(reader)) != -1) { + if(Character.isLetterOrDigit(ch) || ch == '_' || ch == '.' || ch == '$') { + sb.append((char)ch); + } + else { + reader.unread(ch); + break; + } + } + + return sb.toString(); + } + + + /** + * Takes vector of ProtocolConfigurations, iterates through it, creates Protocol for + * each ProtocolConfiguration and returns all Protocols in a vector. + * @param protocol_configs Vector of ProtocolConfigurations + * @param stack The protocol stack + * @return Vector of Protocols + */ + private static Vector createProtocols(Vector protocol_configs, final ProtocolStack stack) throws Exception { + Vector retval=new Vector(); + ProtocolConfiguration protocol_config; + Protocol layer; + String singleton_name; + + for(int i=0; i < protocol_configs.size(); i++) { + protocol_config=protocol_configs.elementAt(i); + singleton_name=protocol_config.getProperties().getProperty(Global.SINGLETON_NAME); + if(singleton_name != null && singleton_name.trim().length() > 0) { + synchronized(stack) { + if(i > 0) { // crude way to check whether protocol is a transport + throw new IllegalArgumentException("Property 'singleton_name' can only be used in a transport" + + " protocol (was used in " + protocol_config.getProtocolName() + ")"); + } + Map> singleton_transports=ProtocolStack.getSingletonTransports(); + Tuple val=singleton_transports.get(singleton_name); + layer=val != null? val.getVal1() : null; + if(layer != null) { + retval.add(layer); + } + else { + layer=protocol_config.createLayer(stack); + if(layer == null) + return null; + singleton_transports.put(singleton_name, new Tuple((TP)layer,(short)0)); + retval.addElement(layer); + } + } + continue; + } + layer=protocol_config.createLayer(stack); + if(layer == null) + return null; + retval.addElement(layer); + } + sanityCheck(retval); + return retval; + } + + + /** + Throws an exception if sanity check fails. Possible sanity check is uniqueness of all protocol names + */ + public static void sanityCheck(Vector protocols) throws Exception { + Vector names=new Vector(); + Protocol prot; + String name; + ProtocolReq req; + Vector req_list=new Vector(); + int evt_type; + + // Checks for unique names + for(int i=0; i < protocols.size(); i++) { + prot=protocols.elementAt(i); + name=prot.getName(); + for(int j=0; j < names.size(); j++) { + if(name.equals(names.elementAt(j))) { + throw new Exception("Configurator.sanityCheck(): protocol name " + name + + " has been used more than once; protocol names have to be unique !"); + } + } + names.addElement(name); + } + + + // Checks whether all requirements of all layers are met + for(int i=0; i < protocols.size(); i++) { + prot=protocols.elementAt(i); + req=new ProtocolReq(prot.getName()); + req.up_reqs=prot.requiredUpServices(); + req.down_reqs=prot.requiredDownServices(); + req.up_provides=prot.providedUpServices(); + req.down_provides=prot.providedDownServices(); + req_list.addElement(req); + } + + + for(int i=0; i < req_list.size(); i++) { + req=req_list.elementAt(i); + + // check whether layers above this one provide corresponding down services + if(req.up_reqs != null) { + for(int j=0; j < req.up_reqs.size(); j++) { + evt_type=((Integer)req.up_reqs.elementAt(j)).intValue(); + + if(!providesDownServices(i, req_list, evt_type)) { + throw new Exception("Configurator.sanityCheck(): event " + + Event.type2String(evt_type) + " is required by " + + req.name + ", but not provided by any of the layers above"); + } + } + } + + // check whether layers below this one provide corresponding up services + if(req.down_reqs != null) { // check whether layers above this one provide up_reqs + for(int j=0; j < req.down_reqs.size(); j++) { + evt_type=((Integer)req.down_reqs.elementAt(j)).intValue(); + + if(!providesUpServices(i, req_list, evt_type)) { + throw new Exception("Configurator.sanityCheck(): event " + + Event.type2String(evt_type) + " is required by " + + req.name + ", but not provided by any of the layers below"); + } + } + } + + } + } + + + /** Check whether any of the protocols 'below' end_index provide evt_type */ + static boolean providesUpServices(int end_index, Vector req_list, int evt_type) { + ProtocolReq req; + + for(int i=0; i < end_index; i++) { + req=(ProtocolReq)req_list.elementAt(i); + if(req.providesUpService(evt_type)) + return true; + } + return false; + } + + + /** Checks whether any of the protocols 'above' start_index provide evt_type */ + static boolean providesDownServices(int start_index, Vector req_list, int evt_type) { + ProtocolReq req; + + for(int i=start_index; i < req_list.size(); i++) { + req=(ProtocolReq)req_list.elementAt(i); + if(req.providesDownService(evt_type)) + return true; + } + return false; + } + + + + /* --------------------------- End of Private Methods ---------------------------------- */ + + + + + + private static class ProtocolReq { + Vector up_reqs=null; + Vector down_reqs=null; + Vector up_provides=null; + Vector down_provides=null; + String name=null; + + ProtocolReq(String name) { + this.name=name; + } + + + boolean providesUpService(int evt_type) { + int type; + + if(up_provides != null) { + for(int i=0; i < up_provides.size(); i++) { + type=((Integer)up_provides.elementAt(i)).intValue(); + if(type == evt_type) + return true; + } + } + return false; + } + + boolean providesDownService(int evt_type) { + int type; + + if(down_provides != null) { + for(int i=0; i < down_provides.size(); i++) { + type=((Integer)down_provides.elementAt(i)).intValue(); + if(type == evt_type) + return true; + } + } + return false; + } + + + public String toString() { + StringBuilder ret=new StringBuilder(); + ret.append('\n' + name + ':'); + if(up_reqs != null) + ret.append("\nRequires from above: " + printUpReqs()); + + if(down_reqs != null) + ret.append("\nRequires from below: " + printDownReqs()); + + if(up_provides != null) + ret.append("\nProvides to above: " + printUpProvides()); + + if(down_provides != null) + ret.append("\nProvides to below: ").append(printDownProvides()); + return ret.toString(); + } + + + String printUpReqs() { + StringBuilder ret; + ret=new StringBuilder("["); + if(up_reqs != null) { + for(int i=0; i < up_reqs.size(); i++) { + ret.append(Event.type2String(((Integer)up_reqs.elementAt(i)).intValue()) + ' '); + } + } + return ret.toString() + ']'; + } + + String printDownReqs() { + StringBuilder ret=new StringBuilder("["); + if(down_reqs != null) { + for(int i=0; i < down_reqs.size(); i++) { + ret.append(Event.type2String(((Integer)down_reqs.elementAt(i)).intValue()) + ' '); + } + } + return ret.toString() + ']'; + } + + + String printUpProvides() { + StringBuilder ret=new StringBuilder("["); + if(up_provides != null) { + for(int i=0; i < up_provides.size(); i++) { + ret.append(Event.type2String(((Integer)up_provides.elementAt(i)).intValue()) + ' '); + } + } + return ret.toString() + ']'; + } + + String printDownProvides() { + StringBuilder ret=new StringBuilder("["); + if(down_provides != null) { + for(int i=0; i < down_provides.size(); i++) + ret.append(Event.type2String(((Integer)down_provides.elementAt(i)).intValue()) + + ' '); + } + return ret.toString() + ']'; + } + + } + + + /** + * Parses and encapsulates the specification for 1 protocol of the protocol stack, e.g. + * UNICAST(timeout=5000) + */ + public static class ProtocolConfiguration { + private String protocol_name=null; + private String properties_str=null; + private final Properties properties=new Properties(); + private static final String protocol_prefix="org.jgroups.protocols"; + + + /** + * Creates a new ProtocolConfiguration. + * @param config_str The configuration specification for the protocol, e.g. + *
        VERIFY_SUSPECT(timeout=1500)
        + */ + public ProtocolConfiguration(String config_str) throws Exception { + setContents(config_str); + } + + public ProtocolConfiguration() { + } + + public String getProtocolName() { + return protocol_name; + } + + public void setProtocolName(String name) { + protocol_name=name; + } + + public Properties getProperties() { + return properties; + } + + public void setPropertiesString(String props) { + this.properties_str=props; + } + + void setContents(String config_str) throws Exception { + int index=config_str.indexOf('('); // e.g. "UDP(in_port=3333)" + int end_index=config_str.lastIndexOf(')'); + + if(index == -1) { + protocol_name=config_str; + } + else { + if(end_index == -1) { + throw new Exception("Configurator.ProtocolConfiguration.setContents(): closing ')' " + + "not found in " + config_str + ": properties cannot be set !"); + } + else { + properties_str=config_str.substring(index + 1, end_index); + protocol_name=config_str.substring(0, index); + } + } + + /* "in_port=5555;out_port=6666" */ + if(properties_str != null) { + String[] components=properties_str.split(";"); + for(int i=0; i < components.length; i++) { + String name, value, comp=components[i]; + index=comp.indexOf('='); + if(index == -1) { + throw new Exception("Configurator.ProtocolConfiguration.setContents(): '=' not found in " + comp); + } + name=comp.substring(0, index); + value=comp.substring(index + 1, comp.length()); + properties.put(name, value); + } + } + } + + + private Protocol createLayer(ProtocolStack prot_stack) throws Exception { + Protocol retval=null; + if(protocol_name == null) + return null; + + String defaultProtocolName=protocol_prefix + '.' + protocol_name; + Class clazz=null; + + try { + clazz=Util.loadClass(defaultProtocolName, this.getClass()); + } + catch(ClassNotFoundException e) { + } + + if(clazz == null) { + try { + clazz=Util.loadClass(protocol_name, this.getClass()); + } + catch(ClassNotFoundException e) { + } + if(clazz == null) { + throw new Exception("unable to load class for protocol " + protocol_name + + " (either as an absolute - " + protocol_name + " - or relative - " + + defaultProtocolName + " - package name)!"); + } + } + + try { + retval=(Protocol)clazz.newInstance(); + + if(retval == null) + throw new Exception("creation of instance for protocol " + protocol_name + "failed !"); + retval.setProtocolStack(prot_stack); + if(properties != null) + if(!retval.setPropertiesInternal(properties)) + throw new IllegalArgumentException("the following properties in " + protocol_name + + " are not recognized: " + properties); + } + catch(InstantiationException inst_ex) { + log.error("an instance of " + protocol_name + " could not be created. Please check that it implements" + + " interface Protocol and that is has a public empty constructor !"); + throw inst_ex; + } + return retval; + } + + + public String toString() { + StringBuilder retval=new StringBuilder(); + retval.append("Protocol: "); + if(protocol_name == null) + retval.append(""); + else + retval.append(protocol_name); + if(properties != null) + retval.append("(" + properties + ')'); + return retval.toString(); + } + } + + +} + + Index: 3rdParty_sources/jgroups/org/jgroups/stack/ExponentialInterval.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/stack/ExponentialInterval.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/stack/ExponentialInterval.java 17 Aug 2012 14:51:27 -0000 1.1 @@ -0,0 +1,33 @@ +package org.jgroups.stack; + +/** + * @author Bela Ban + * @version $Id: ExponentialInterval.java,v 1.1 2012/08/17 14:51:27 marcin Exp $ + */ +public class ExponentialInterval implements Interval { + private long value=30; + private static final long MAX=15000; + + public ExponentialInterval() { + + } + + public ExponentialInterval(long value) { + this.value=value; + } + + public long next() { + long retval=value; + value=Math.min(MAX, value * 2); + return retval; + } + + /** We don't need to copy as we don't have any state */ + public final Interval copy() { + return new ExponentialInterval(value); + } + + public String toString() { + return String.valueOf(value); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/stack/GossipClient.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/stack/GossipClient.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/stack/GossipClient.java 17 Aug 2012 14:51:27 -0000 1.1 @@ -0,0 +1,479 @@ +package org.jgroups.stack; + + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.Address; +import org.jgroups.util.Util; + +import java.io.*; +import java.net.InetAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.InetSocketAddress; +import java.util.*; + + +/** + * Local stub for clients to access one (or more) GossipRouters. Will use proprietary protocol + * (using GossipData PDUs) based on TCP to connect to GossipRouter.

        + * Requires JDK >= 1.3 due to the use of Timer. + * + * @author Bela Ban Oct 4 2001 + * @version $Id: GossipClient.java,v 1.1 2012/08/17 14:51:27 marcin Exp $ + */ +public class GossipClient { + Timer timer=new Timer(true); + + /** Hashtable> */ + final Map> groups=new Hashtable>(); // groups - List of Addresses + private Refresher refresher_task=new Refresher(); + final Vector

        gossip_servers=new Vector
        (); // a list of GossipRouters (IpAddress) + boolean timer_running=false; + boolean refresher_enabled=true; + long refresh_interval=20000; // interval for re-registering; must be less than in GossipRouter + int sock_conn_timeout=2000; // max number of ms to wait for socket establishment to GossipRouter + int sock_read_timeout=0; // max number of ms to wait for socket reads (0 means block forever, or until the sock is closed) + + protected final Log log=LogFactory.getLog(this.getClass()); + + + /** + * Creates the GossipClient + * @param gossip_host The address and port of the host on which the GossipRouter is running + * @param expiry Interval (in msecs) for the refresher task + */ + public GossipClient(IpAddress gossip_host, long expiry) { + init(gossip_host, expiry); + } + + + /** + Creates the GossipClient + @param gossip_hosts List of IpAddresses + @param expiry Interval (in msecs) for the refresher task + */ + public GossipClient(Vector gossip_hosts, long expiry) { + if(gossip_hosts == null) { + if(log.isErrorEnabled()) log.error("empty set of GossipRouters given"); + return; + } + for(IpAddress host: gossip_hosts) + init(host, expiry); + } + + + public GossipClient(Vector gossip_hosts, long expiry, int sock_conn_timeout) { + this(gossip_hosts, expiry); + this.sock_conn_timeout=sock_conn_timeout; + } + + public boolean isRefresherEnabled() { + return refresher_enabled; + } + + public void setRefresherEnabled(boolean refresher_enabled) { + this.refresher_enabled=refresher_enabled; + } + + + public int getSocketConnectionTimeout() { + return sock_conn_timeout; + } + + public void setSocketConnectionTimeout(int sock_conn_timeout) { + this.sock_conn_timeout=sock_conn_timeout; + } + + public int getSocketReadTimeout() { + return sock_read_timeout; + } + + public void setSocketReadTimeout(int sock_read_timeout) { + this.sock_read_timeout=sock_read_timeout; + } + + public long getRefreshInterval() { + return refresh_interval; + } + + public void setRefreshInterval(long refresh_interval) { + this.refresh_interval=refresh_interval; + } + + public void stop() { + timer_running=false; + if(refresher_task != null) + refresher_task.cancel(); + timer.cancel(); + groups.clear(); + // provide another refresh tools in case the channel gets reconnected + // timer=new Timer(); + // refresher_task=new Refresher(); + } + + + public void destroy() { + timer_running=false; + timer.cancel(); + groups.clear(); + } + + + /** + * Adds a GossipRouter to be accessed. + */ + public void addGossipRouter(IpAddress gossip_host) { + if(!gossip_servers.contains(gossip_host)) + gossip_servers.addElement(gossip_host); + } + + + /** + Adds the member to the given group. If the group already has an entry for the member, + its timestamp will be updated, preventing the cache cleaner from removing the entry.

        + The entry will be registered with all GossipRouters that GossipClient is configured to access + */ + public void register(String group, Address mbr) { + if(group == null || mbr == null) { + if(log.isErrorEnabled()) log.error("group or mbr is null"); + return; + } + + List

        mbrs=groups.get(group); + if(mbrs == null) { + mbrs=new LinkedList
        (); + mbrs.add(mbr); + groups.put(group, mbrs); + } + else { + if(!mbrs.contains(mbr)) + mbrs.add(mbr); + } + + _register(group, mbr); // update entry in GossipRouter + + if(refresher_enabled) { + if(!timer_running) { + timer=new Timer(true); + refresher_task=new Refresher(); + timer.schedule(refresher_task, refresh_interval, refresh_interval); + timer_running=true; + } + } + } + + + public void unregister(String group, Address mbr) { + if(group == null || mbr == null) { + if(log.isErrorEnabled()) log.error("group or mbr is null"); + return; + } + + _unregister(group, mbr); // remove entry from GossipRouter + } + + + /** + Returns all members of a given group + @param group The group name + @return List A list of Addresses + */ + public List
        getMembers(String group) { + if(group == null) { + if(log.isErrorEnabled()) log.error("group is null"); + return null; + } + List
        result=_getMembers(group); + if(log.isTraceEnabled()) + log.trace("GET(" + group + ") --> " + result); + return result; + } + + + + /* ------------------------------------- Private methods ----------------------------------- */ + + + final void init(IpAddress gossip_host, long refresh_interval) { + this.refresh_interval=refresh_interval; + addGossipRouter(gossip_host); + } + + + /** + * Registers the group|mbr with *all* GossipRouters. + */ + void _register(String group, Address mbr) { + Socket sock=null; + DataOutputStream out=null; + IpAddress entry; + GossipData gossip_req; + + for(int i=0; i < gossip_servers.size(); i++) { + entry=(IpAddress) gossip_servers.elementAt(i); + if(entry.getIpAddress() == null || entry.getPort() == 0) { + if(log.isErrorEnabled()) log.error("entry.host or entry.port is null"); + continue; + } + try { + if(log.isTraceEnabled()) + log.trace("REGISTER(" + group + ", " + mbr + ") with GossipRouter at " + entry.getIpAddress() + ':' + entry.getPort()); + sock=new Socket(); + if(sock_read_timeout > 0) + sock.setSoTimeout(sock_read_timeout); + sock.connect(new InetSocketAddress(entry.getIpAddress(), entry.getPort()), sock_conn_timeout); + out=new DataOutputStream(sock.getOutputStream()); + gossip_req=new GossipData(GossipRouter.REGISTER, group, mbr, null); + // must send GossipData as fast as possible, otherwise the request might be rejected + gossip_req.writeTo(out); + out.flush(); + } + catch(Exception ex) { + if(log.isErrorEnabled()) log.error("exception connecting to host " + entry); + } + finally { + Util.close(out); + Util.close(sock); + } + } + } + + + void _unregister(String group, Address mbr) { + Socket sock=null; + DataOutputStream out=null; + IpAddress entry; + GossipData gossip_req; + + for(int i=0; i < gossip_servers.size(); i++) { + entry=(IpAddress) gossip_servers.elementAt(i); + if(entry.getIpAddress() == null || entry.getPort() == 0) { + if(log.isErrorEnabled()) log.error("entry.host or entry.port is null"); + continue; + } + try { + if(log.isTraceEnabled()) + log.trace("UNREGISTER(" + group + ", " + mbr + ") with GossipRouter at " + entry.getIpAddress() + ':' + entry.getPort()); + sock=new Socket(); + if(sock_read_timeout > 0) + sock.setSoTimeout(sock_read_timeout); + sock.connect(new InetSocketAddress(entry.getIpAddress(), entry.getPort()), sock_conn_timeout); + out=new DataOutputStream(sock.getOutputStream()); + gossip_req=new GossipData(GossipRouter.UNREGISTER, group, mbr, null); + // must send GossipData as fast as possible, otherwise the + // request might be rejected + gossip_req.writeTo(out); + out.flush(); + } + catch(Exception ex) { + if(log.isErrorEnabled()) log.error("exception connecting to host " + entry); + } + finally { + Util.close(out); + if(sock != null) { + try {sock.close();} catch(IOException e) {} + } + } + } + } + + + /** + * Sends a GET_MBR_REQ to *all* GossipRouters, merges responses. + */ + private List
        _getMembers(String group) { + List
        ret=new LinkedList
        (); + Socket sock=null; + SocketAddress destAddr; + DataOutputStream out=null; + DataInputStream in=null; + IpAddress entry; + GossipData gossip_req, gossip_rsp; + Address mbr; + + for(int i=0; i < gossip_servers.size(); i++) { + entry=(IpAddress) gossip_servers.elementAt(i); + if(entry.getIpAddress() == null || entry.getPort() == 0) { + if(log.isErrorEnabled()) log.error("entry.host or entry.port is null"); + continue; + } + + try { + sock=new Socket(); + if(sock_read_timeout > 0) + sock.setSoTimeout(sock_read_timeout); + destAddr=new InetSocketAddress(entry.getIpAddress(), entry.getPort()); + sock.connect(destAddr, sock_conn_timeout); + out=new DataOutputStream(sock.getOutputStream()); + + gossip_req=new GossipData(GossipRouter.GOSSIP_GET, group, null, null); + // must send GossipData as fast as possible, otherwise the + // request might be rejected + gossip_req.writeTo(out); + out.flush(); + + in=new DataInputStream(sock.getInputStream()); + gossip_rsp=new GossipData(); + gossip_rsp.readFrom(in); + if(gossip_rsp.mbrs != null) { // merge with ret + for(Iterator it=gossip_rsp.mbrs.iterator(); it.hasNext();) { + mbr=(Address)it.next(); + if(!ret.contains(mbr)) + ret.add(mbr); + } + } + } + catch(Exception ex) { + if(log.isErrorEnabled()) log.error("exception connecting to host " + entry); + } + finally { + Util.close(out); + Util.close(in); + Util.close(sock); + } + } + + return ret; + } + + + + /* ---------------------------------- End of Private methods ------------------------------- */ + + + + /** + * Periodically iterates through groups and refreshes all registrations with GossipRouter + */ + private class Refresher extends TimerTask { + + public void run() { + int num_items=0; + String group; + List
        mbrs; + + if(log.isTraceEnabled()) log.trace("refresher task is run"); + for(Map.Entry> entry: groups.entrySet()) { + group=entry.getKey(); + mbrs=entry.getValue(); + if(mbrs != null) { + for(Address mbr: mbrs) { + if(log.isTraceEnabled()) log.trace("registering " + group + " : " + mbr); + register(group, mbr); + num_items++; + } + } + } + if(log.isTraceEnabled()) log.trace("refresher task done. Registered " + num_items + " items"); + } + + } + + + public static void main(String[] args) { + Vector gossip_hosts=new Vector(); + String host; + InetAddress ip_addr; + int port; + boolean get=false, register=false, keep_running=false; + String register_host=null; + int register_port=0; + String get_group=null, register_group=null; + GossipClient gossip_client=null; + List mbrs; + long expiry=20000; + int sock_conn_timeout=2000; + int sock_read_timeout=3000; + + + for(int i=0; i < args.length; i++) { + if("-help".equals(args[i])) { + usage(); + return; + } + if("-expiry".equals(args[i])) { + expiry=Long.parseLong(args[++i]); + continue; + } + if("-sock_read_timeout".equals(args[i])) { + sock_read_timeout=Integer.parseInt(args[++i]); + continue; + } + if("-sock_conn_timeout".equals(args[i])) { + sock_conn_timeout=Integer.parseInt(args[++i]); + continue; + } + if("-host".equals(args[i])) { + host=args[++i]; + port=Integer.parseInt(args[++i]); + try { + ip_addr=InetAddress.getByName(host); + gossip_hosts.add(new IpAddress(ip_addr, port)); + } + catch(Exception ex) { + System.err.println(ex); + } + continue; + } + if("-keep_running".equals(args[i])) { + keep_running=true; + continue; + } + if("-get".equals(args[i])) { + get=true; + get_group=args[++i]; + continue; + } + if("-register".equals(args[i])) { + register_group=args[++i]; + register_host=args[++i]; + register_port=Integer.parseInt(args[++i]); + register=true; + continue; + } + usage(); + return; + } + + if(gossip_hosts.isEmpty()) { + System.err.println("At least 1 GossipRouter has to be given"); + return; + } + + if(!register && !get) { + System.err.println("Neither get nor register command given, will not do anything"); + return; + } + + try { + gossip_client=new GossipClient(gossip_hosts, expiry); + gossip_client.setSocketConnectionTimeout(sock_conn_timeout); + gossip_client.setSocketReadTimeout(sock_read_timeout); + if(register) { + System.out.println("Registering " + register_group + " --> " + register_host + ':' + register_port); + gossip_client.register(register_group, new IpAddress(register_host, register_port)); + } + + if(get) { + System.out.println("Getting members for group " + get_group); + mbrs=gossip_client.getMembers(get_group); + System.out.println("Members for group " + get_group + " are " + mbrs); + } + } + catch(Exception ex) { + System.err.println(ex); + } + if(!keep_running) + gossip_client.stop(); + } + + + static void usage() { + System.out.println("GossipClient [-help] [-host ]+ " + + "[-sock_conn_timeout ] [-sock_read_timeout ] " + + " [-get ] [-register ] [-expiry ] " + + "[-keep_running]]"); + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/stack/GossipData.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/stack/GossipData.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/stack/GossipData.java 17 Aug 2012 14:51:27 -0000 1.1 @@ -0,0 +1,75 @@ +// $Id: GossipData.java,v 1.1 2012/08/17 14:51:27 marcin Exp $ + +package org.jgroups.stack; + + +import org.jgroups.Address; +import org.jgroups.util.Streamable; +import org.jgroups.util.Util; + +import java.io.*; +import java.util.Vector; +import java.util.List; +import java.util.ArrayList; +import java.util.LinkedList; + + +/** + * Encapsulates data sent between GossipRouter and GossipClient + * @author Bela Ban Oct 4 2001 + */ +public class GossipData implements Streamable { + byte type=0; // One of GossipRouter type, e.g. CONNECT, REGISTER etc + String group=null; // CONNECT, GET_REQ and GET_RSP + Address addr=null; // CONNECT + List mbrs=null; // GET_RSP + + public GossipData() { // for streamable + } + + public GossipData(byte type) { + this.type=type; + } + + public GossipData(byte type, String group, Address addr, List mbrs) { + this.type=type; + this.group=group; + this.addr=addr; + this.mbrs=mbrs; + } + + + public byte getType() {return type;} + public String getGroup() {return group;} + public Address getAddress() {return addr;} + public List getMembers() {return mbrs;} + + public void setMembers(List mbrs) { + this.mbrs=mbrs; + } + + + public String toString() { + StringBuilder sb=new StringBuilder(); + sb.append(GossipRouter.type2String(type)).append( "(").append("group=").append(group).append(", addr=").append(addr); + sb.append(", mbrs=").append(mbrs); + return sb.toString(); + } + + + public void writeTo(DataOutputStream out) throws IOException { + out.writeByte(type); + Util.writeString(group, out); + Util.writeAddress(addr, out); + Util.writeAddresses(mbrs, out); + } + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + type=in.readByte(); + group=Util.readString(in); + addr=Util.readAddress(in); + mbrs=(List)Util.readAddresses(in, LinkedList.class); + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/stack/GossipRouter.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/stack/GossipRouter.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/stack/GossipRouter.java 17 Aug 2012 14:51:28 -0000 1.1 @@ -0,0 +1,1230 @@ + +package org.jgroups.stack; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.Date; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Scanner; +import java.util.Timer; +import java.util.TimerTask; +import java.util.Map.Entry; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.Address; +import org.jgroups.Version; +import org.jgroups.util.DefaultThreadFactory; +import org.jgroups.util.ShutdownRejectedExecutionHandler; +import org.jgroups.util.ThreadFactory; +import org.jgroups.util.ThreadManagerThreadPoolExecutor; +import org.jgroups.util.Util; + +/** + * Router for TCP based group comunication (using layer TCP instead of UDP). + * Instead of the TCP layer sending packets point-to-point to each other + * member, it sends the packet to the router which - depending on the target + * address - multicasts or unicasts it to the group / or single member.

        + * This class is especially interesting for applets which cannot directly make + * connections (neither UDP nor TCP) to a host different from the one they were + * loaded from. Therefore, an applet would create a normal channel plus + * protocol stack, but the bottom layer would have to be the TCP layer which + * sends all packets point-to-point (over a TCP connection) to the router, + * which in turn forwards them to their end location(s) (also over TCP). A + * centralized router would therefore have to be running on the host the applet + * was loaded from.

        + * An alternative for running JGroups in an applet (IP multicast is not allows + * in applets as of 1.2), is to use point-to-point UDP communication via the + * gossip server. However, then the appplet has to be signed which involves + * additional administrative effort on the part of the user.

        + * @author Bela Ban + * @author Ovidiu Feodorov + * @version $Id: GossipRouter.java,v 1.1 2012/08/17 14:51:28 marcin Exp $ + * @since 2.1.1 + */ +public class GossipRouter { + public static final byte CONNECT=1; // CONNECT(group, addr) --> local address + public static final byte DISCONNECT=2; // DISCONNECT(group, addr) + public static final byte REGISTER=3; // REGISTER(group, addr) + public static final byte GOSSIP_GET=4; // GET(group) --> List (members) + public static final byte ROUTER_GET=5; // GET(group) --> List (members) + public static final byte GET_RSP=6; // GET_RSP(List) + public static final byte UNREGISTER=7; // UNREGISTER(group, addr) + public static final byte DUMP=8; // DUMP + public static final byte SHUTDOWN=9; + + public static final int PORT=12001; + public static final long EXPIRY_TIME=30000; + public static final long GOSSIP_REQUEST_TIMEOUT=1000; + public static final long ROUTING_CLIENT_REPLY_TIMEOUT=120000; + + private int port; + + private String bindAddressString; + + // time (in msecs) until a cached 'gossip' member entry expires + private long expiryTime; + + // number of millisecs the main thread waits to receive a gossip request + // after connection was established; upon expiration, the router initiates + // the routing protocol on the connection. Don't set the interval too big, + // otherwise the router will appear slow in answering routing requests. + @Deprecated + private long gossipRequestTimeout; + + // time (in ms) main thread waits for a router client to send the routing + // request type and the group afiliation before it declares the request + // failed. + @Deprecated + private long routingClientReplyTimeout; + + // HashMap >. Maintains associations between groups and their members. Keys=group + // names, values = maps of logical address / AddressEntry associations + private final ConcurrentMap> routingTable=new ConcurrentHashMap>(); + + private ServerSocket srvSock=null; + private InetAddress bindAddress=null; + + /** Time (in millis) for setting SO_LINGER on sockets returned from accept(). 0 means don't set SO_LINGER */ + private long linger_timeout=2000L; + + /** Time (in millis) for SO_TIMEOUT on sockets returned from accept(). 0 means don't set SO_TIMEOUT */ + private long sock_read_timeout=3000L; + + /** The max queue size of backlogged connections */ + private int backlog=1000; + + private boolean up=true; + private boolean discard_loopbacks=false; + protected int thread_pool_min_threads=2; + + protected int thread_pool_max_threads=10; + + protected long thread_pool_keep_alive_time=30000; + protected boolean thread_pool_enabled=false; + protected boolean thread_pool_queue_enabled=true; + protected int thread_pool_queue_max_size=50; + + protected String thread_pool_rejection_policy="Run"; + + protected ExecutorService thread_pool; + + protected BlockingQueue thread_pool_queue=null; + + protected ThreadFactory default_thread_factory = new DefaultThreadFactory(Util.getGlobalThreadGroup(), "gossip-handlers", true, true); + + + // the cache sweeper + protected Timer timer=null; + + protected final Log log=LogFactory.getLog(this.getClass()); + + // + // JMX INSTRUMENTATION - MANAGEMENT INTERFACE + // + + public GossipRouter() { + this(PORT); + } + + public GossipRouter(int port) { + this(port, null); + } + + public GossipRouter(int port, String bindAddressString) { + this(port, bindAddressString, EXPIRY_TIME); + } + + public GossipRouter(int port, String bindAddressString, + long expiryTime) { + this(port, bindAddressString, expiryTime, + GOSSIP_REQUEST_TIMEOUT, + ROUTING_CLIENT_REPLY_TIMEOUT); + } + + /** + * + * Creates a gossip router on a given port bound to a specified interface + * and an expiry time (in msecs) until a cached 'gossip' member entry + * expires. + * + *

        + * Remaining two parameters are deprecated and not used. + * + * @param port + * @param bindAddressString + * @param expiryTime + * @param gossipRequestTimeout + * @param routingClientReplyTimeout + * + */ + public GossipRouter(int port, String bindAddressString, + long expiryTime, long gossipRequestTimeout, + long routingClientReplyTimeout) { + this.port=port; + this.bindAddressString=bindAddressString; + this.expiryTime=expiryTime; + this.gossipRequestTimeout=gossipRequestTimeout; + this.routingClientReplyTimeout=routingClientReplyTimeout; + } + + // + // MANAGED ATTRIBUTES + // + + public void setPort(int port) { + this.port=port; + } + + public int getPort() { + return port; + } + + public void setBindAddress(String bindAddress) { + bindAddressString=bindAddress; + } + + public String getBindAddress() { + return bindAddressString; + } + + public int getBacklog() { + return backlog; + } + + public void setBacklog(int backlog) { + this.backlog=backlog; + } + + public void setExpiryTime(long expiryTime) { + this.expiryTime=expiryTime; + } + + public long getExpiryTime() { + return expiryTime; + } + + @Deprecated + public void setGossipRequestTimeout(long gossipRequestTimeout) { + this.gossipRequestTimeout=gossipRequestTimeout; + } + + @Deprecated + public long getGossipRequestTimeout() { + return gossipRequestTimeout; + } + + @Deprecated + public void setRoutingClientReplyTimeout(long routingClientReplyTimeout) { + this.routingClientReplyTimeout=routingClientReplyTimeout; + } + + @Deprecated + public long getRoutingClientReplyTimeout() { + return routingClientReplyTimeout; + } + + public boolean isStarted() { + return srvSock != null; + } + + public boolean isDiscardLoopbacks() { + return discard_loopbacks; + } + + public void setDiscardLoopbacks(boolean discard_loopbacks) { + this.discard_loopbacks=discard_loopbacks; + } + + public long getLingerTimeout() { + return linger_timeout; + } + + public void setLingerTimeout(long linger_timeout) { + this.linger_timeout=linger_timeout; + } + + public long getSocketReadTimeout() { + return sock_read_timeout; + } + + public void setSocketReadTimeout(long sock_read_timeout) { + this.sock_read_timeout=sock_read_timeout; + } + + public ThreadFactory getDefaultThreadPoolThreadFactory() { + return default_thread_factory; + } + + public void setDefaultThreadPoolThreadFactory(ThreadFactory factory) { + default_thread_factory=factory; + if(thread_pool instanceof ThreadPoolExecutor) + ((ThreadPoolExecutor)thread_pool).setThreadFactory(factory); + } + + public int getThreadPoolMinThreads() { + return thread_pool_min_threads; + } + + public void setThreadPoolMinThreads(int thread_pool_min_threads) { + this.thread_pool_min_threads = thread_pool_min_threads; + } + + public int getThreadPoolMaxThreads() { + return thread_pool_max_threads; + } + + public void setThreadPoolMaxThreads(int thread_pool_max_threads) { + this.thread_pool_max_threads = thread_pool_max_threads; + } + + public long getThreadPoolKeepAliveTime() { + return thread_pool_keep_alive_time; + } + + public void setThreadPoolKeepAliveTime(long thread_pool_keep_alive_time) { + this.thread_pool_keep_alive_time = thread_pool_keep_alive_time; + } + + public boolean isThreadPoolEnabled() { + return thread_pool_enabled; + } + + public void setThreadPoolEnabled(boolean thread_pool_enabled) { + this.thread_pool_enabled = thread_pool_enabled; + } + + public boolean isThreadPoolQueueEnabled() { + return thread_pool_queue_enabled; + } + + public void setThreadPoolQueueEnabled(boolean thread_pool_queue_enabled) { + this.thread_pool_queue_enabled = thread_pool_queue_enabled; + } + + public int getThreadPoolQueueMaxSize() { + return thread_pool_queue_max_size; + } + + public void setThreadPoolQueueMaxSize(int thread_pool_queue_max_size) { + this.thread_pool_queue_max_size = thread_pool_queue_max_size; + } + + public String getThreadPoolRejectionPolicy() { + return thread_pool_rejection_policy; + } + + public void setThreadPoolRejectionPolicy(String thread_pool_rejection_policy) { + this.thread_pool_rejection_policy = thread_pool_rejection_policy; + } + + + public static String type2String(int type) { + switch(type) { + case CONNECT: + return "CONNECT"; + case DISCONNECT: + return "DISCONNECT"; + case REGISTER: + return "REGISTER"; + case GOSSIP_GET: + return "GOSSIP_GET"; + case ROUTER_GET: + return "ROUTER_GET"; + case GET_RSP: + return "GET_RSP"; + case UNREGISTER: + return "UNREGISTER"; + case DUMP: + return "DUMP"; + case SHUTDOWN: + return "SHUTDOWN"; + default: + return "unknown"; + } + } + + // + // JBoss MBean LIFECYCLE OPERATIONS + // + + + /** + * JBoss MBean lifecycle operation. + */ + public void create() throws Exception { + // not used + } + + /** + * JBoss MBean lifecycle operation. Called after create(). When this method + * is called, the managed attributes have already been set.
        + * Brings the Router in fully functional state. + */ + public void start() throws Exception { + if(srvSock != null) { + throw new Exception("Router already started."); + } + + if(bindAddressString != null) { + bindAddress=InetAddress.getByName(bindAddressString); + srvSock=new ServerSocket(port, backlog, bindAddress); + } + else { + srvSock=new ServerSocket(port, backlog); + } + + up=true; + + if (thread_pool_enabled) { + if (thread_pool_queue_enabled) { + thread_pool_queue = new LinkedBlockingQueue( + thread_pool_queue_max_size); + } + else { + thread_pool_queue = new SynchronousQueue(); + } + thread_pool = createThreadPool(thread_pool_min_threads, + thread_pool_max_threads, thread_pool_keep_alive_time, + thread_pool_rejection_policy, thread_pool_queue, + default_thread_factory); + } + else { + thread_pool = Executors.newSingleThreadExecutor(default_thread_factory); + } + + Runtime.getRuntime().addShutdownHook(new Thread() { + public void run() { + cleanup(); + GossipRouter.this.stop(); + } + }); + + // start the main server thread + new Thread(new Runnable() { + public void run() { + mainLoop(); + cleanup(); + } + }, "GossipRouter").start(); + + // starts the cache sweeper as daemon thread, so we won't block on it + // upon termination + timer=new Timer(true); + timer.schedule(new TimerTask() { + public void run() { + sweep(); + } + }, expiryTime, expiryTime); + } + + /** + * JBoss MBean lifecycle operation. The JMX agent allways calls this method + * before destroy(). Close connections and frees resources. + */ + public void stop() { + up=false; + thread_pool.shutdownNow(); + + timer.cancel(); + if (srvSock != null) { + shutdown(); + try { + srvSock.close(); + } catch (Exception e) { + if (log.isErrorEnabled()) + log.error("Failed to close server socket: " + e); + } + // exiting the mainLoop will clean the tables + srvSock = null; + } + if(log.isInfoEnabled()) log.info("router stopped"); + } + + /** + * JBoss MBean lifecycle operation. + */ + public void destroy() { + // not used + } + + // + // ORDINARY OPERATIONS + // + + + + public String dumpRoutingTable() { + String label="routing"; + StringBuilder sb=new StringBuilder(); + + if(routingTable.isEmpty()) { + sb.append("empty ").append(label).append(" table"); + } + else { + for(Iterator i=routingTable.keySet().iterator(); i.hasNext();) { + String gname=i.next(); + sb.append("GROUP: '" + gname + "'\n"); + Map map=routingTable.get(gname); + if(map == null) { + sb.append("\tnull list of addresses\n"); + } + else if(map.isEmpty()) { + sb.append("\tempty list of addresses\n"); + } + else { + for(Iterator j=map.values().iterator(); j.hasNext();) { + sb.append('\t').append(i.next()).append('\n'); + } + } + } + } + return sb.toString(); + } + + + /** + * The main server loop. Runs on the JGroups Router Main Thread. + */ + private void mainLoop() { + + if (bindAddress == null) { + bindAddress = srvSock.getInetAddress(); + } + + printStartupInfo(); + + while (up && srvSock != null) { + try { + final Socket sock = srvSock.accept(); + if (linger_timeout > 0) { + int linger = Math.max(1, (int) (linger_timeout / 1000)); + sock.setSoLinger(true, linger); + } + if (sock_read_timeout > 0) { + sock.setSoTimeout((int) sock_read_timeout); + } + + final DataInputStream input = new DataInputStream(sock.getInputStream()); + Runnable task = new Runnable(){ + + public void run() { + DataOutputStream output =null; + Address peer_addr = null, mbr, logical_addr; + String group; + GossipData req = new GossipData(); + try { + req.readFrom(input); + + switch (req.getType()) { + case GossipRouter.REGISTER: + mbr = req.getAddress(); + group = req.getGroup(); + if (log.isTraceEnabled()) + log.trace("REGISTER(" + group + ", " + mbr + ")"); + if (group == null || mbr == null) { + if (log.isErrorEnabled()) + log.error("group or member is null, cannot register member"); + } else + addGossipEntry(group, mbr, new AddressEntry(mbr)); + Util.close(input); + Util.close(sock); + break; + + case GossipRouter.UNREGISTER: + mbr = req.getAddress(); + group = req.getGroup(); + if (log.isTraceEnabled()) + log.trace("UNREGISTER(" + group + ", " + mbr + ")"); + if (group == null || mbr == null) { + if (log.isErrorEnabled()) + log.error("group or member is null, cannot unregister member"); + } else + removeGossipEntry(group, mbr); + Util.close(input); + Util.close(output); + Util.close(sock); + break; + + case GossipRouter.GOSSIP_GET: + group = req.getGroup(); + List

        mbrs = null; + Map map; + map = routingTable.get(group); + if (map != null) { + mbrs = new LinkedList
        (map.keySet()); + } + + if (log.isTraceEnabled()) + log.trace("GOSSIP_GET(" + group + ") --> " + mbrs); + output = new DataOutputStream(sock.getOutputStream()); + GossipData rsp = new GossipData(GossipRouter.GET_RSP,group, null, mbrs); + rsp.writeTo(output); + Util.close(input); + Util.close(output); + Util.close(sock); + break; + + case GossipRouter.ROUTER_GET: + group = req.getGroup(); + output = new DataOutputStream(sock.getOutputStream()); + + List
        ret = null; + map = routingTable.get(group); + if (map != null) { + ret = new LinkedList
        (map.keySet()); + } else + ret = new LinkedList
        (); + if (log.isTraceEnabled()) + log.trace("ROUTER_GET(" + group + ") --> " + ret); + rsp = new GossipData(GossipRouter.GET_RSP, group, null, + ret); + rsp.writeTo(output); + Util.close(input); + Util.close(output); + Util.close(sock); + break; + + case GossipRouter.DUMP: + output = new DataOutputStream(sock.getOutputStream()); + output.writeUTF(dumpRoutingTable()); + Util.close(input); + Util.close(output); + Util.close(sock); + break; + + case GossipRouter.CONNECT: + sock.setSoTimeout(0); // we have to disable this here + output = new DataOutputStream(sock.getOutputStream()); + peer_addr = new IpAddress(sock.getInetAddress(), sock.getPort()); + logical_addr = req.getAddress(); + String group_name = req.getGroup(); + + if (log.isTraceEnabled()) + log.trace("CONNECT(" + group_name + ", "+ logical_addr + ")"); + SocketThread st = new SocketThread(sock, input,group_name, logical_addr); + addEntry(group_name, logical_addr, new AddressEntry(logical_addr, peer_addr, sock, st, output)); + st.start(); + break; + + case GossipRouter.DISCONNECT: + Address addr = req.getAddress(); + group_name = req.getGroup(); + removeEntry(group_name, addr); + if (log.isTraceEnabled()) + log.trace("DISCONNECT(" + group_name + ", " + addr+ ")"); + Util.close(input); + Util.close(output); + Util.close(sock); + break; + + case GossipRouter.SHUTDOWN: + if (log.isInfoEnabled()) + log.info("router shutting down"); + Util.close(input); + Util.close(output); + Util.close(sock); + up = false; + break; + default: + if (log.isWarnEnabled()) + log.warn("received unkown gossip request (gossip="+ req + ')'); + break; + } + } catch (Exception e) { + if (up) + if (log.isErrorEnabled()) + log.error("failure handling a client request", e); + Util.close(input); + Util.close(output); + Util.close(sock); + } + }}; + if(!thread_pool.isShutdown()) + thread_pool.submit(task); + else + task.run(); + } catch (Exception exc) { + if (log.isErrorEnabled()) + log.error("failure receiving and setting up a client request", exc); + } + } + } + + protected ExecutorService createThreadPool(int min_threads, + int max_threads, long keep_alive_time, String rejection_policy, + BlockingQueue queue, final ThreadFactory factory) { + + ThreadPoolExecutor pool = new ThreadManagerThreadPoolExecutor( + min_threads, max_threads, keep_alive_time, + TimeUnit.MILLISECONDS, queue); + pool.setThreadFactory(factory); + + // default + RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy(); + if (rejection_policy != null) { + if (rejection_policy.equals("abort")) + handler = new ThreadPoolExecutor.AbortPolicy(); + else if (rejection_policy.equals("discard")) + handler = new ThreadPoolExecutor.DiscardPolicy(); + else if (rejection_policy.equals("discardoldest")) + handler = new ThreadPoolExecutor.DiscardOldestPolicy(); + } + pool.setRejectedExecutionHandler(new ShutdownRejectedExecutionHandler( + handler)); + + return pool; + } + + + + /** + * Cleans the routing tables while the Router is going down. + */ + private void cleanup() { + // shutdown the routing threads and cleanup the tables + for(Map map: routingTable.values()) { + if(map != null) { + for(AddressEntry entry: map.values()) { + entry.destroy(); + } + } + } + routingTable.clear(); + } + + /** + * Connects to the ServerSocket and sends the shutdown header. + */ + private void shutdown() { + Socket s=null; + DataOutputStream dos=null; + try { + s=new Socket(srvSock.getInetAddress(),srvSock.getLocalPort()); + dos=new DataOutputStream(s.getOutputStream()); + dos.writeInt(SHUTDOWN); + dos.writeUTF(""); + } + catch(Exception e) { + if(log.isErrorEnabled()) log.error("shutdown failed: " + e); + } + finally { + Util.close(s); + Util.close(dos); + } + } + + + + + /** + * Removes expired gossip entries (entries older than EXPIRY_TIME msec). + * @since 2.2.1 + */ + private void sweep() { + long diff, currentTime=System.currentTimeMillis(); + int num_entries_removed=0; + + for(Iterator>> it=routingTable.entrySet().iterator(); it.hasNext();) { + Entry > entry=it.next(); + Map map=entry.getValue(); + if(map == null || map.isEmpty()) { + it.remove(); + continue; + } + for(Iterator> it2=map.entrySet().iterator(); it2.hasNext();) { + Entryentry2=it2.next(); + AddressEntry ae=entry2.getValue(); + diff=currentTime - ae.timestamp; + if(diff > expiryTime) { + it2.remove(); + if(log.isTraceEnabled()) + log.trace("removed " + ae.logical_addr + " (" + diff + " msecs old)"); + num_entries_removed++; + } + } + } + + if(num_entries_removed > 0) { + if(log.isTraceEnabled()) log.trace("done (removed " + num_entries_removed + " entries)"); + } + } + + + + + private void route(Address dest, String dest_group, byte[] msg, Address sender) { + //if(log.isTraceEnabled()) { + // int len=msg != null? msg.length : 0; + //log.trace("routing request from " + sender + " for " + dest_group + " to " + + // (dest == null? "ALL" : dest.toString()) + ", " + len + " bytes"); + //} + + if(dest == null) { // send to all members in group dest.getChannelName() + if(dest_group == null) { + if(log.isErrorEnabled()) log.error("both dest address and group are null"); + } + else { + sendToAllMembersInGroup(dest_group, msg, sender); + } + } + else { + // send to destination address + AddressEntry ae=findAddressEntry(dest_group, dest); + if(ae == null) { + if(log.isTraceEnabled()) + log.trace("cannot find " + dest + " in the routing table, \nrouting table=\n" + dumpRoutingTable()); + return; + } + if(ae.output == null) { + if(log.isErrorEnabled()) log.error(dest + " is associated with a null output stream"); + return; + } + try { + sendToMember(dest, ae.output, msg, sender); + } + catch(Exception e) { + if(log.isErrorEnabled()) log.error("failed sending message to " + dest + ": " + e.getMessage()); + removeEntry(dest_group, dest); // will close socket + } + } + } + + + + private void addEntry(String groupname, Address logical_addr, AddressEntry entry) { + addEntry(groupname, logical_addr, entry, false); + } + + + /** + * Adds a new member to the routing group. + */ + private void addEntry(String groupname, Address logical_addr, AddressEntry entry, boolean update_only) { + if(groupname == null || logical_addr == null) { + if(log.isErrorEnabled()) log.error("groupname or logical_addr was null, entry was not added"); + return; + } + + ConcurrentMap mbrs=routingTable.get(groupname); + if(mbrs == null) { + mbrs=new ConcurrentHashMap(); + mbrs.put(logical_addr, entry); + routingTable.putIfAbsent(groupname, mbrs); + } + else { + AddressEntry tmp=mbrs.get(logical_addr); + if(tmp != null) { // already present + if(update_only) { + tmp.update(); + return; + } + tmp.destroy(); + } + mbrs.put(logical_addr, entry); + } + } + + + + private void removeEntry(String groupname, Address logical_addr) { + Mapval=routingTable.get(groupname); + if(val == null) + return; + synchronized(val) { + AddressEntry entry=val.get(logical_addr); + if(entry != null) { + entry.destroy(); + val.remove(logical_addr); + } + } + } + + + /** + * @return null if not found + */ + private AddressEntry findAddressEntry(String group_name, Address logical_addr) { + if(group_name == null || logical_addr == null) + return null; + Map val=routingTable.get(group_name); + if(val == null) + return null; + return (AddressEntry)val.get(logical_addr); + } + + + + /** + * Adds a new member to the group in the gossip table or renews the + * membership where is the case. + * @since 2.2.1 + */ + private void addGossipEntry(String groupname, Address logical_addr, AddressEntry e) { + addEntry(groupname, logical_addr, e, true); + } + + + private void removeGossipEntry(String groupname, Address mbr) { + removeEntry(groupname, mbr); + } + + + + private void sendToAllMembersInGroup(String groupname, byte[] msg, Address sender) { + Map val = routingTable.get(groupname); + if(val == null || val.isEmpty()) + return; + + synchronized(val) { + for(Iterator> i=val.entrySet().iterator(); i.hasNext();) { + Entry tmp=i.next(); + AddressEntry entry=tmp.getValue(); + DataOutputStream dos=entry.output; + + if(dos != null) { + // send only to 'connected' members + try { + sendToMember(null, dos, msg, sender); + } + catch(Exception e) { + if(log.isWarnEnabled()) log.warn("cannot send to " + entry.logical_addr + ": " + e.getMessage()); + entry.destroy(); // this closes the socket + i.remove(); + } + } + } + } + } + + + /** + * @throws IOException + */ + private void sendToMember(Address dest, DataOutputStream out, byte[] msg, Address sender) throws IOException { + if(out == null) + return; + + if(discard_loopbacks && dest != null && dest.equals(sender)) { + return; + } + + synchronized(out) { + Util.writeAddress(dest, out); + out.writeInt(msg.length); + out.write(msg, 0, msg.length); + } + } + + + /** + * Prints startup information. + */ + private void printStartupInfo() { + System.out.println("GossipRouter started at " + new Date()); + + System.out.print("Listening on port " + port); + System.out.println(" bound on address " + bindAddress); + + System.out.print("Backlog is " + backlog); + System.out.print(", linger timeout is " + linger_timeout); + System.out.println(", and read timeout is " + sock_read_timeout); + } + + + /** + * Class used to store Addresses in both routing and gossip tables. + * If it is used for routing, sock and output have valid values, otherwise + * they're null and only the timestamp counts. + */ + class AddressEntry { + Address logical_addr=null, physical_addr=null; + Socket sock=null; + DataOutputStream output=null; + long timestamp=0; + final SocketThread thread; + + /** + * AddressEntry for a 'gossip' membership. + */ + public AddressEntry(Address addr) { + this(addr, null, null, null, null); + } + + public AddressEntry(Address logical_addr, Address physical_addr, Socket sock, SocketThread thread, DataOutputStream output) { + this.logical_addr=logical_addr; + this.physical_addr=physical_addr; + this.sock=sock; + this.thread=thread; + this.output=output; + this.timestamp=System.currentTimeMillis(); + } + + void destroy() { + if(thread != null) { + thread.finish(); + } + Util.close(output); + output=null; + Util.close(sock); + sock=null; + timestamp=0; + } + + public void update() { + timestamp=System.currentTimeMillis(); + } + + public boolean equals(Object other) { + return other instanceof AddressEntry && logical_addr.equals(((AddressEntry)other).logical_addr); + } + + public String toString() { + StringBuilder sb=new StringBuilder("logical addr="); + sb.append(logical_addr).append(" (").append(physical_addr).append(")"); + //if(sock != null) { + // sb.append(", sock="); + //sb.append(sock); + //} + if(timestamp > 0) { + long diff=System.currentTimeMillis() - timestamp; + sb.append(", ").append(diff).append(" ms old"); + } + return sb.toString(); + } + } + + + private static int threadCounter=0; + + + /** + * A SocketThread manages one connection to a client. Its main task is message routing. + */ + class SocketThread extends Thread { + private volatile boolean active=true; + Socket sock=null; + DataInputStream input=null; + Address logical_addr=null; + String group_name=null; + + + public SocketThread(Socket sock, DataInputStream ois, String group_name, Address logical_addr) { + super(Util.getGlobalThreadGroup(), "SocketThread " + (threadCounter++)); + this.sock=sock; + input=ois; + this.group_name=group_name; + this.logical_addr=logical_addr; + } + + void closeSocket() { + Util.close(input); + Util.close(sock); + } + + void finish() { + active=false; + } + + + public void run() { + byte[] buf; + int len; + Address dst_addr=null; + String gname; + + DataOutputStream output = null; + try { + output=new DataOutputStream(sock.getOutputStream()); + output.writeBoolean(true); + } + catch(IOException e1) { + try { + output.writeBoolean(false); + } + catch(IOException e) {} + } + + while(active) { + try { + // 1. Group name is first + gname=input.readUTF(); + + // 2. Second is the destination address + dst_addr=Util.readAddress(input); + + // 3. Then the length of the byte buffer representing the message + len=input.readInt(); + if(len == 0) { + if(log.isWarnEnabled()) log.warn("received null message"); + continue; + } + + // 4. Finally the message itself, as a byte buffer + buf=new byte[len]; + input.readFully(buf, 0, buf.length); // message + } + catch(Exception io_ex) { + if(log.isTraceEnabled()) + log.trace(sock.getInetAddress().getHostName() + ':' + sock.getPort() + + " closed connection; removing it from routing table"); + removeEntry(group_name, logical_addr); // will close socket + return; + } + + try { + route(dst_addr, gname, buf, logical_addr); + } + catch(Exception e) { + if(log.isErrorEnabled()) log.error("failed routing request to " + dst_addr, e); + break; + } + } + closeSocket(); + } + + } + + + public static void main(String[] args) throws Exception { + String arg; + int port=12001; + long expiry=GossipRouter.EXPIRY_TIME; + long timeout=GossipRouter.GOSSIP_REQUEST_TIMEOUT; + long routingTimeout=GossipRouter.ROUTING_CLIENT_REPLY_TIMEOUT; + + int backlog = 0; + long soLinger = -1; + long soTimeout = -1; + + GossipRouter router=null; + String bind_addr=null; + + for(int i=0; i < args.length; i++) { + arg=args[i]; + if("-port".equals(arg)) { + port=Integer.parseInt(args[++i]); + continue; + } + if("-bindaddress".equals(arg) || "-bind_addr".equals(arg)) { + bind_addr=args[++i]; + continue; + } + if ("-backlog".equals(arg)) { + backlog=Integer.parseInt(args[++i]); + continue; + } + if("-expiry".equals(arg)) { + expiry=Long.parseLong(args[++i]); + continue; + } + + // this option is not used and should be deprecated/removed + // in a future release + if("-timeout".equals(arg)) { + System.out.println(" -timeout is depracted and will be ignored"); + ++i; + continue; + } + // this option is not used and should be deprecated/removed + // in a future release + if("-rtimeout".equals(arg)) { + System.out.println(" -rtimeout is depracted and will be ignored"); + ++i; + continue; + } + if ("-solinger".equals(arg)) { + soLinger=Long.parseLong(args[++i]); + continue; + } + if ("-sotimeout".equals(arg)) { + soTimeout=Long.parseLong(args[++i]); + continue; + } + + help(); + return; + } + System.out.println("GossipRouter is starting (JGroups version: " + Version.printVersion() + ")"); + + try { + router=new GossipRouter(port, bind_addr, expiry); + + if (backlog > 0) + router.setBacklog(backlog); + + if (soTimeout >= 0) + router.setSocketReadTimeout(soTimeout); + + if (soLinger >= 0) + router.setLingerTimeout(soLinger); + + router.start(); + } + catch(Exception e) { + System.err.println(e); + } + String quit = ""; + while (!quit.startsWith("quit")) { + Scanner in = new Scanner(System.in); + try{ + quit = in.nextLine(); + } + catch(Exception e){} + } + + router.stop(); + router.cleanup(); + } + + static void help() { + System.out.println(); + System.out.println("GossipRouter [-port ] [-bind_addr
        ] [options]"); + System.out.println(); + System.out.println("Options:"); + System.out.println(); + + // -timeout isn't used and should be deprecated/removed + // in a future release. It's left in this code for backwards + // compatability, but it is no longer advertized. + //System.out.println(" -timeout - Number of millisecs the router waits to receive"); + //System.out.println(" a gossip request after connection was established;"); + //System.out.println(" upon expiration, the router initiates the routing"); + //System.out.println(" protocol on the connection."); + + System.out.println(" -backlog - Max queue size of backlogged connections. Must be"); + System.out.println(" greater than zero or the default of 1000 will be"); + System.out.println(" used."); + System.out.println(); + System.out.println(" -expiry - Time until a gossip cache entry expires. 30000 is"); + System.out.println(" the default value."); + System.out.println(); + System.out.println(" -solinger - Time for setting SO_LINGER on connections. 0"); + System.out.println(" means do not set SO_LINGER. Must be greater than"); + System.out.println(" or equal to zero or the default of 2000 will be"); + System.out.println(" used."); + System.out.println(); + System.out.println(" -sotimeout - Time for setting SO_TIMEOUT on connections. 0"); + System.out.println(" means don't set SO_TIMEOUT. Must be greater than"); + System.out.println(" or equal to zero or the default of 3000 will be"); + System.out.println(" used."); + System.out.println(); + } +} \ No newline at end of file Index: 3rdParty_sources/jgroups/org/jgroups/stack/Interval.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/stack/Interval.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/stack/Interval.java 17 Aug 2012 14:51:28 -0000 1.1 @@ -0,0 +1,20 @@ + +package org.jgroups.stack; + +import org.jgroups.annotations.Immutable; +import org.jgroups.annotations.GuardedBy; + + +/** + * Interface which returns a time series, one value at a time calling next() + * @author Bela Ban + * @version $Id: Interval.java,v 1.1 2012/08/17 14:51:28 marcin Exp $ + */ +public interface Interval { + /** @return the next interval */ + public long next() ; + + /** Returns a copy of the state. If there is no state, this method may return a ref to itself */ + Interval copy(); +} + Index: 3rdParty_sources/jgroups/org/jgroups/stack/IpAddress.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/stack/IpAddress.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/stack/IpAddress.java 17 Aug 2012 14:51:27 -0000 1.1 @@ -0,0 +1,358 @@ +// $Id: IpAddress.java,v 1.1 2012/08/17 14:51:27 marcin Exp $ + +package org.jgroups.stack; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.Address; +import org.jgroups.Global; +import org.jgroups.util.Util; + +import java.io.*; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; + + +/** + * Network-dependent address (Internet). Generated by the bottommost layer of the protocol + * stack (UDP). Contains an InetAddress and port. + * @author Bela Ban + */ +public class IpAddress implements Address { + + private static final long serialVersionUID = 2592301708270771474L; + + private InetAddress ip_addr=null; + private int port=0; + private byte[] additional_data; + protected static final Log log=LogFactory.getLog(IpAddress.class); + static boolean resolve_dns=false; + transient int size=-1; + + static { + /* Trying to get value of resolve_dns. PropertyPermission not granted if + * running in an untrusted environment with JNLP */ + try { + String tmp=Util.getProperty(new String[]{Global.RESOLVE_DNS, "resolve.dns"}, null, null, false, "false"); + resolve_dns=Boolean.valueOf(tmp).booleanValue(); + } + catch (SecurityException ex){ + resolve_dns=false; + } + } + + + + // Used only by Externalization + public IpAddress() { + } + + public IpAddress(String i, int p) throws UnknownHostException { + port=p; + ip_addr=InetAddress.getByName(i); + } + + + + public IpAddress(InetAddress i, int p) { + ip_addr=i; port=p; + if(this.ip_addr == null) + setAddressToLocalHost(); + } + + + private void setAddressToLocalHost() { + try { + ip_addr=InetAddress.getLocalHost(); // get first NIC found (on multi-homed systems) + // size=size(); + } + catch(Exception e) { + if(log.isWarnEnabled()) log.warn("exception: " + e); + } + } + + public IpAddress(int port) { + this(port, true); + } + + public IpAddress(int port, boolean set_default_host) { + this.port=port; + if(set_default_host) + setAddressToLocalHost(); + } + + + public IpAddress(InetSocketAddress sock_addr) { + port=sock_addr.getPort(); + ip_addr=sock_addr.getAddress(); + } + + + + public final InetAddress getIpAddress() {return ip_addr;} + public final int getPort() {return port;} + + public final boolean isMulticastAddress() { + return ip_addr != null && ip_addr.isMulticastAddress(); + } + + /** + * Returns the additional_data. + * @return byte[] + */ + public final byte[] getAdditionalData() { + return additional_data; + } + + /** + * Sets the additional_data. + * @param additional_data The additional_data to set + */ + public final void setAdditionalData(byte[] additional_data) { + this.additional_data=additional_data; + size=-1; // changed May 13 2006 bela (suggested by Bruce Schuchardt) + size=size(); + } + + + /** + * Establishes an order between 2 addresses. Assumes other contains non-null IpAddress. + * Excludes channel_name from comparison. + * @return 0 for equality, value less than 0 if smaller, greater than 0 if greater. + * @deprecated Use {@link #compareTo(org.jgroups.Address)} instead + */ + public final int compare(IpAddress other) { + return compareTo(other); + } + + + /** + * implements the java.lang.Comparable interface + * @see java.lang.Comparable + * @param o - the Object to be compared + * @return a negative integer, zero, or a positive integer as this object is less than, + * equal to, or greater than the specified object. + * @exception java.lang.ClassCastException - if the specified object's type prevents it + * from being compared to this Object. + */ + public final int compareTo(Object o) { + int h1, h2, rc; // added Nov 7 2005, makes sense with canonical addresses + + if(this == o) return 0; + if(!(o instanceof IpAddress)) + throw new ClassCastException("comparison between different classes: the other object is " + + (o != null? o.getClass() : o)); + IpAddress other = (IpAddress) o; + if(ip_addr == null) + if (other.ip_addr == null) return port < other.port ? -1 : (port > other.port ? 1 : 0); + else return -1; + + h1=ip_addr.hashCode(); + h2=other.ip_addr.hashCode(); + rc=h1 < h2? -1 : h1 > h2? 1 : 0; + return rc != 0 ? rc : port < other.port ? -1 : (port > other.port ? 1 : 0); + } + + + /** + * This method compares both addresses' dotted-decimal notation in string format if the hashcode and ports are + * identical. Ca 30% slower than {@link #compareTo(Object)} if used excessively. + * @param o + * @return + * @deprecated Use {@link #compareTo(org.jgroups.Address)} instead + */ + public final int compareToUnique(Object o) { + int h1, h2, rc; // added Nov 7 2005, makes sense with canonical addresses + + if(this == o) return 0; + if ((o == null) || !(o instanceof IpAddress)) + throw new ClassCastException("comparison between different classes: the other object is " + + (o != null? o.getClass() : o)); + IpAddress other = (IpAddress) o; + if(ip_addr == null) + if (other.ip_addr == null) return port < other.port ? -1 : (port > other.port ? 1 : 0); + else return -1; + + h1=ip_addr.hashCode(); + h2=other.ip_addr.hashCode(); + rc=h1 < h2? -1 : h1 > h2? 1 : 0; + + if(rc != 0) + return rc; + + rc=port < other.port ? -1 : (port > other.port ? 1 : 0); + + if(rc != 0) + return rc; + + // here we have the same addresses hash codes and ports, now let's compare the dotted-decimal addresses + + String addr1=ip_addr.getHostAddress(), addr2=other.ip_addr.getHostAddress(); + return addr1.compareTo(addr2); + } + + + + public final boolean equals(Object obj) { + if(this == obj) return true; // added Nov 7 2005, makes sense with canonical addresses + + if(!(obj instanceof IpAddress)) + return false; + IpAddress other=(IpAddress)obj; + boolean sameIP=false; + if(this.ip_addr != null) + sameIP=this.ip_addr.equals(other.ip_addr); + else + sameIP=(other.ip_addr == null); + return sameIP && (this.port == other.port); + } + + + + + public final int hashCode() { + return ip_addr != null ? ip_addr.hashCode() + port : port; + } + + + + + public String toString() { + StringBuilder sb=new StringBuilder(); + + if(ip_addr == null) + sb.append(""); + else { + if(ip_addr.isMulticastAddress()) + sb.append(ip_addr.getHostAddress()); + else { + String host_name=null; + if(resolve_dns) { + host_name=ip_addr.getHostName(); + // appendShortName(host_name, sb); + } + else { + host_name=ip_addr.getHostAddress(); + } + sb.append(host_name); + } + } + sb.append(":").append(port); + return sb.toString(); + } + + + + public void writeExternal(ObjectOutput out) throws IOException { + if(ip_addr != null) { + byte[] address=ip_addr.getAddress(); + out.writeByte(address.length); // 1 byte + out.write(address, 0, address.length); + } + else { + out.writeByte(0); + } + out.writeShort(port); + if(additional_data != null) { + out.writeBoolean(true); + out.writeShort(additional_data.length); + out.write(additional_data, 0, additional_data.length); + } + else + out.writeBoolean(false); + } + + + + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + int len=in.readByte(); + if(len > 0) { + //read the four bytes + byte[] a = new byte[len]; + //in theory readFully(byte[]) should be faster + //than read(byte[]) since latter reads + // 4 bytes one at a time + in.readFully(a); + //look up an instance in the cache + this.ip_addr=InetAddress.getByAddress(a); + } + //then read the port + port=in.readUnsignedShort(); + + if(in.readBoolean() == false) + return; + len=in.readShort(); + if(len > 0) { + additional_data=new byte[len]; + in.readFully(additional_data, 0, additional_data.length); + } + } + + public void writeTo(DataOutputStream out) throws IOException { + if(ip_addr != null) { + byte[] address=ip_addr.getAddress(); // 4 bytes (IPv4) or 16 bytes (IPv6) + out.writeByte(address.length); // 1 byte + out.write(address, 0, address.length); + } + else { + out.writeByte(0); + } + out.writeShort(port); + if(additional_data != null) { + out.writeBoolean(true); // 1 byte + out.writeShort(additional_data.length); + out.write(additional_data, 0, additional_data.length); + } + else { + out.writeBoolean(false); + } + } + + public void readFrom(DataInputStream in) throws IOException { + int len=in.readByte(); + if(len > 0 && (len != Global.IPV4_SIZE && len != Global.IPV6_SIZE)) + throw new IOException("length has to be " + Global.IPV4_SIZE + " or " + Global.IPV6_SIZE + " bytes (was " + + len + " bytes)"); + byte[] a = new byte[len]; // 4 bytes (IPv4) or 16 bytes (IPv6) + in.readFully(a); + this.ip_addr=InetAddress.getByAddress(a); + + // changed from readShort(): we need the full 65535, with a short we'd only get up to 32K ! + port=in.readUnsignedShort(); + + if(in.readBoolean() == false) + return; + len=in.readUnsignedShort(); + if(len > 0) { + additional_data=new byte[len]; + in.readFully(additional_data, 0, additional_data.length); + } + } + + public int size() { + if(size >= 0) + return size; + // length (1 bytes) + 4 bytes for port + 1 for additional_data available + int tmp_size=Global.BYTE_SIZE+ Global.SHORT_SIZE + Global.BYTE_SIZE; + if(ip_addr != null) + tmp_size+=ip_addr.getAddress().length; // 4 bytes for IPv4 + if(additional_data != null) + tmp_size+=additional_data.length+Global.SHORT_SIZE; + size=tmp_size; + return tmp_size; + } + + public Object clone() throws CloneNotSupportedException { + IpAddress ret=new IpAddress(ip_addr, port); + if(additional_data != null) { + ret.additional_data=new byte[additional_data.length]; + System.arraycopy(additional_data, 0, ret.additional_data, 0, additional_data.length); + } + return ret; + } + + + +} Index: 3rdParty_sources/jgroups/org/jgroups/stack/NakReceiverWindow.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/stack/NakReceiverWindow.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/stack/NakReceiverWindow.java 17 Aug 2012 14:51:28 -0000 1.1 @@ -0,0 +1,599 @@ + + + +package org.jgroups.stack; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.Address; +import org.jgroups.Message; +import org.jgroups.annotations.GuardedBy; +import org.jgroups.util.TimeScheduler; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.concurrent.locks.ReentrantLock; + + +/** + * Keeps track of messages according to their sequence numbers. Allows + * messages to be added out of order, and with gaps between sequence numbers. + * Method remove() removes the first message with a sequence + * number that is 1 higher than next_to_remove (this variable is + * then incremented), or it returns null if no message is present, or if no + * message's sequence number is 1 higher. + *

        + * When there is a gap upon adding a message, its seqno will be added to the + * Retransmitter, which (using a timer) requests retransmissions of missing + * messages and keeps on trying until the message has been received, or the + * member who sent the message is suspected. + * + * There are 3 variables which keep track of messages: + *

          + *
        • low: lowest seqno, modified on stable(). On stable(), we purge msgs [low digest.highest_delivered] + *
        • highest_delivered: the highest delivered seqno, updated on remove(). The next message to be removed is highest_delivered + 1 + *
        • highest_received: the highest received message, updated on add (if a new message is added, not updated e.g. + * if a missing msg was received) + *
        + *

        + * Note that the first seqno expected is 1. This design is described in doc/design.NAKACK.txt + *

        + * Example: + * 1,2,3,5,6,8: low=1, highest_delivered=2 (or 3, depending on whether remove() was called !), highest_received=8 + * + * @author Bela Ban May 27 1999, May 2004, Jan 2007 + * @author John Georgiadis May 8 2001 + * @version $Id: NakReceiverWindow.java,v 1.1 2012/08/17 14:51:28 marcin Exp $ + */ +public class NakReceiverWindow { + + public interface Listener { + void missingMessageReceived(long seqno, Address original_sender); + + void messageGapDetected(long from, long to, Address src); + } + + /** dummy for null values: ConcurrentHashMap doesn't allow null values */ + public static final Message NULL_MSG=new Message(false); + + private final ReadWriteLock lock=new ReentrantReadWriteLock(); + + private final ReentrantLock up_lock=new ReentrantLock(); + + Address local_addr=null; + + + /** Lowest seqno, modified on stable(). On stable(), we purge msgs [low digest.highest_delivered] */ + @GuardedBy("lock") + private long low=0; + + /** The highest delivered seqno, updated on remove(). The next message to be removed is highest_delivered + 1 */ + @GuardedBy("lock") + private long highest_delivered=0; + + /** The highest received message, updated on add (if a new message is added, not updated e.g. if a missing msg + * was received) */ + @GuardedBy("lock") + private long highest_received=0; + + + /** ConcurrentMap. Maintains messages keyed by (sorted) sequence numbers */ + private final ConcurrentMap xmit_table=new ConcurrentHashMap(); + + /** + * Messages that have been received in order are sent up the stack (= delivered to the application). Delivered + * messages are removed from NakReceiverWindow.xmit_table and moved to NakReceiverWindow.delivered_msgs, where + * they are later garbage collected (by STABLE). Since we do retransmits only from sent messages, never + * received or delivered messages, we can turn the moving to delivered_msgs off, so we don't keep the message + * around, and don't need to wait for garbage collection to remove them. + */ + private boolean discard_delivered_msgs=false; + + + /** If value is > 0, the retransmit buffer is bounded: only the max_xmit_buf_size latest messages are kept, + * older ones are discarded when the buffer size is exceeded. A value <= 0 means unbounded buffers + */ + private int max_xmit_buf_size=0; + + /** if not set, no retransmitter thread will be started. Useful if + * protocols do their own retransmission (e.g PBCAST) */ + private Retransmitter retransmitter=null; + + private Listener listener=null; + + protected static final Log log=LogFactory.getLog(NakReceiverWindow.class); + + /** The highest stable() seqno received */ + long highest_stability_seqno=0; + + /** The loss rate (70% of the new value and 30% of the old value) */ + private double smoothed_loss_rate=0.0; + + + /** + * Creates a new instance with the given retransmit command + * + * @param sender The sender associated with this instance + * @param cmd The command used to retransmit a missing message, will + * be invoked by the table. If null, the retransmit thread will not be started + * @param highest_delivered_seqno The next seqno to remove is highest_delivered_seqno +1 + * @param lowest_seqno The low seqno purged + * @param sched the external scheduler to use for retransmission + * requests of missing msgs. If it's not provided or is null, an internal + */ + public NakReceiverWindow(Address sender, Retransmitter.RetransmitCommand cmd, long highest_delivered_seqno, long lowest_seqno, TimeScheduler sched) { + this(null, sender, cmd, highest_delivered_seqno, lowest_seqno, sched); + } + + + public NakReceiverWindow(Address local_addr, Address sender, Retransmitter.RetransmitCommand cmd, long highest_delivered_seqno, long lowest_seqno, TimeScheduler sched) { + this.local_addr=local_addr; + highest_delivered=highest_delivered_seqno; + highest_received=highest_delivered; + low=Math.min(lowest_seqno, highest_delivered); + + if(cmd != null) + retransmitter=sched == null ? + new Retransmitter(sender, cmd) : + new Retransmitter(sender, cmd, sched); + } + + + /** + * Creates a new instance with the given retransmit command + * + * @param sender The sender associated with this instance + * @param cmd The command used to retransmit a missing message, will + * be invoked by the table. If null, the retransmit thread will not be started + * @param highest_delivered_seqno The next seqno to remove is highest_delivered_seqno +1 + * @param sched the external scheduler to use for retransmission + * requests of missing msgs. If it's not provided or is null, an internal + */ + public NakReceiverWindow(Address sender, Retransmitter.RetransmitCommand cmd, long highest_delivered_seqno, TimeScheduler sched) { + this(sender, cmd, highest_delivered_seqno, 0, sched); + } + + /** + * Creates a new instance with the given retransmit command + * + * @param sender The sender associated with this instance + * @param cmd The command used to retransmit a missing message, will + * be invoked by the table. If null, the retransmit thread will not be started + * @param highest_delivered_seqno The next seqno to remove is highest_delivered_seqno +1 + */ + public NakReceiverWindow(Address sender, Retransmitter.RetransmitCommand cmd, long highest_delivered_seqno) { + this(sender, cmd, highest_delivered_seqno, null); + } + + public ReentrantLock getLock() { + return up_lock; + } + + public void setRetransmitTimeouts(Interval timeouts) { + retransmitter.setRetransmitTimeouts(timeouts); + } + + + public void setDiscardDeliveredMessages(boolean flag) { + this.discard_delivered_msgs=flag; + } + + public int getMaxXmitBufSize() { + return max_xmit_buf_size; + } + + public void setMaxXmitBufSize(int max_xmit_buf_size) { + this.max_xmit_buf_size=max_xmit_buf_size; + } + + public void setListener(Listener l) { + this.listener=l; + } + + public int getPendingXmits() { + return retransmitter!= null? retransmitter.size() : 0; + } + + /** + * Returns the loss rate, which is defined as the number of pending retransmission requests / the total number of + * messages in xmit_table + * @return The loss rate + */ + public double getLossRate() { + int total_msgs=size(); + int pending_xmits=getPendingXmits(); + if(pending_xmits == 0 || total_msgs == 0) + return 0.0; + + return pending_xmits / (double)total_msgs; + } + + public double getSmoothedLossRate() { + return smoothed_loss_rate; + } + + /** Set the new smoothed_loss_rate value to 70% of the new value and 30% of the old value */ + private void setSmoothedLossRate() { + double new_loss_rate=getLossRate(); + if(smoothed_loss_rate == 0) { + smoothed_loss_rate=new_loss_rate; + } + else { + smoothed_loss_rate=smoothed_loss_rate * .3 + new_loss_rate * .7; + } + } + + + /** + * Adds a message according to its seqno (sequence number). + *

        + * There are 4 cases where messages are added: + *

          + *
        1. seqno is the next to be expected seqno: added to map + *
        2. seqno is <= highest_delivered: discard as we've already delivered it + *
        3. seqno is smaller than the next expected seqno: missing message, add it + *
        4. seqno is greater than the next expected seqno: add it to map and fill the gaps with null messages + * for retransmission. Add the seqno to the retransmitter too + *
        + * @return True if the message was added successfully, false otherwise (e.g. duplicate message) + */ + public boolean add(long seqno, Message msg) { + long old_next, next_to_add; + boolean retval=true; + + lock.writeLock().lock(); + try { + next_to_add=highest_received +1; + old_next=next_to_add; + + // Case #1: we received the expected seqno: most common path + if(seqno == next_to_add) { + xmit_table.put(new Long(seqno), msg); + return true; + } + + // Case #2: we received a message that has already been delivered: discard it + if(seqno <= highest_delivered) { + if(log.isTraceEnabled()) { + StringBuilder sb=new StringBuilder("seqno "); + sb.append(seqno).append(" is smaller than ").append(next_to_add).append("); discarding message"); + log.trace(sb); + } + return false; + } + + // Case #3: we finally received a missing message. // Case #2 handled seqno <= highest_delivered, so this + // seqno *must* be between highest_delivered and next_to_add + if(seqno < next_to_add) { + Object val=xmit_table.get(new Long(seqno)); + if(val == NULL_MSG) { + // only set message if not yet received (bela July 23 2003) + xmit_table.put(seqno, msg); + int num_xmits=retransmitter.remove(seqno); + if(log.isTraceEnabled()) + log.trace(new StringBuilder("added missing msg ").append(msg.getSrc()).append('#').append(seqno)); + if(listener != null && num_xmits > 0) { + try {listener.missingMessageReceived(seqno, msg.getSrc());} catch(Throwable t) {} + } + return true; + } + else + return false; + } + + // Case #4: we received a seqno higher than expected: add NULL_MSG values for missing messages, add to Retransmitter + if(seqno > next_to_add) { + if(listener != null) { + try {listener.messageGapDetected(next_to_add, seqno, msg.getSrc());} catch(Throwable t) {} + } + + for(long i=next_to_add; i < seqno; i++) { // add all msgs (missing or not) into table + xmit_table.put(i, NULL_MSG); + } + xmit_table.put(seqno, msg); + retransmitter.add(old_next, seqno -1); // BUT: add only null messages to xmitter + return true; + } + } + finally { + highest_received=Math.max(highest_received, seqno); + // setSmoothedLossRate(); + lock.writeLock().unlock(); + } + return retval; + } + + + + public Message remove() { + Message retval=null; + + lock.writeLock().lock(); + long next_to_remove=highest_delivered +1; + try { + while(!xmit_table.isEmpty()) { + retval=xmit_table.get(next_to_remove); + if(retval == null) + return null; + if(retval != NULL_MSG) { // message exists and is ready for delivery + if(discard_delivered_msgs) { + Address sender=retval.getSrc(); + if(!local_addr.equals(sender)) { // don't remove if we sent the message ! + xmit_table.remove(next_to_remove); + // low=next_to_remove; + } + } + highest_delivered=next_to_remove; + return retval; + } + else { // message has not yet been received (gap in the message sequence stream) + // drop all messages that have not been received + if(max_xmit_buf_size > 0 && xmit_table.size() > max_xmit_buf_size) { + if(discard_delivered_msgs) { + Address sender=retval.getSrc(); + if(!local_addr.equals(sender)) { // don't remove if we sent the message ! + xmit_table.remove(next_to_remove); + // low=next_to_remove; + } + } + highest_delivered=next_to_remove; + retransmitter.remove(next_to_remove); + } + else { + return null; + } + } + } + return retval; + } + finally { + // setSmoothedLossRate(); + lock.writeLock().unlock(); + } + } + + + public Message removeOOBMessage() { + lock.writeLock().lock(); + try { + long next_to_remove=highest_delivered +1; + Message retval=xmit_table.get(next_to_remove); + if(retval != null && retval.isFlagSet(Message.OOB)) { + return remove(); + } + return null; + } + finally { + lock.writeLock().unlock(); + } + } + + public boolean hasMessagesToRemove() { + lock.readLock().lock(); + try { + Message retval=xmit_table.get(highest_delivered + 1); + return retval != null && !retval.equals(NULL_MSG); + } + finally { + lock.readLock().unlock(); + } + } + + + /** + * Delete all messages <= seqno (they are stable, that is, have been received at all members). + * Stop when a number > seqno is encountered (all messages are ordered on seqnos). + */ + public void stable(long seqno) { + lock.writeLock().lock(); + try { + if(seqno > highest_delivered) { + if(log.isErrorEnabled()) + log.error("seqno " + seqno + " is > highest_delivered " + highest_delivered + "; ignoring stability message"); + return; + } + + // we need to remove all seqnos *including* seqno + if(!xmit_table.isEmpty()) { + for(long i=low; i <= seqno; i++) { + xmit_table.remove(i); + } + } + // remove all seqnos below seqno from retransmission + for(long i=low; i <= seqno; i++) { + retransmitter.remove(i); + } + + highest_stability_seqno=Math.max(highest_stability_seqno, seqno); + low=Math.max(low, seqno); + } + finally { + lock.writeLock().unlock(); + } + } + + + /** + * Reset the retransmitter and the nak window
        + */ + public void reset() { + lock.writeLock().lock(); + try { + retransmitter.reset(); + _reset(); + } + finally { + lock.writeLock().unlock(); + } + } + + + /** + * Stop the retransmitter and reset the nak window
        + */ + public void destroy() { + lock.writeLock().lock(); + try { + retransmitter.stop(); + _reset(); + } + finally { + lock.writeLock().unlock(); + } + } + + + + + + /** + * @return the lowest sequence number of a message that has been + * delivered or is a candidate for delivery (by the next call to + * remove()) + */ + public long getLowestSeen() { + lock.readLock().lock(); + try { + return low; + } + finally { + lock.readLock().unlock(); + } + } + + + /** Returns the highest sequence number of a message consumed by the application (by remove()). + * Note that this is different from the highest deliverable seqno. E.g. in 23,24,26,27,29, the highest + * delivered message may be 22, whereas the highest deliverable message may be 24 ! + * @return the highest sequence number of a message consumed by the + * application (by remove()) + */ + public long getHighestDelivered() { + lock.readLock().lock(); + try { + return highest_delivered; + } + finally { + lock.readLock().unlock(); + } + } + + + + + /** + * Returns the highest sequence number received so far (which may be + * higher than the highest seqno delivered so far; e.g., for + * 1,2,3,5,6 it would be 6. + * + * @see NakReceiverWindow#getHighestDelivered + */ + public long getHighestReceived() { + lock.readLock().lock(); + try { + return highest_received; + } + finally { + lock.readLock().unlock(); + } + } + + + /** + * Returns the message from xmit_table + * @param seqno + * @return Message from xmit_table + */ + public Message get(long seqno) { + lock.readLock().lock(); + try { + return xmit_table.get(seqno); + } + finally { + lock.readLock().unlock(); + } + } + + + public int size() { + lock.readLock().lock(); + try { + return xmit_table.size(); + } + finally { + lock.readLock().unlock(); + } + } + + + public String toString() { + lock.readLock().lock(); + try { + return printMessages(); + } + finally { + lock.readLock().unlock(); + } + } + + + + + /** + * Prints xmit_table. Requires read lock to be present + * @return String + */ + String printMessages() { + StringBuilder sb=new StringBuilder(); + sb.append('[').append(low).append(" : ").append(highest_delivered).append(" (").append(highest_received).append(")"); + if(xmit_table != null && !xmit_table.isEmpty()) { + int non_received=0; + + for(Map.Entry entry: xmit_table.entrySet()) { + if(entry.getValue() == NULL_MSG) + non_received++; + } + sb.append(" (size=").append(xmit_table.size()).append(", missing=").append(non_received). + append(", highest stability=").append(highest_stability_seqno).append(')'); + } + sb.append(']'); + return sb.toString(); + } + + public String printLossRate() { + StringBuilder sb=new StringBuilder(); + int num_missing=getPendingXmits(); + int num_received=size(); + int total=num_missing + num_received; + sb.append("total=").append(total).append(" (received=").append(num_received).append(", missing=") + .append(num_missing).append("), loss rate=").append(getLossRate()) + .append(", smoothed loss rate=").append(smoothed_loss_rate); + return sb.toString(); + } + + /* ------------------------------- Private Methods -------------------------------------- */ + + + + + + /** + * Reset the Nak window. Should be called from within a writeLock() context. + *

        + * i. Delete all received entries
        + * ii. Reset all indices
        + */ + private void _reset() { + xmit_table.clear(); + low=0; + highest_delivered=0; // next (=first) to deliver will be 1 + highest_received=0; + highest_stability_seqno=0; + } + /* --------------------------- End of Private Methods ----------------------------------- */ + + +} Index: 3rdParty_sources/jgroups/org/jgroups/stack/Protocol.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/stack/Protocol.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/stack/Protocol.java 17 Aug 2012 14:51:28 -0000 1.1 @@ -0,0 +1,280 @@ + + +package org.jgroups.stack; + + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.Event; +import org.jgroups.util.ThreadFactory; +import org.jgroups.protocols.TP; + +import java.util.Map; +import java.util.Properties; +import java.util.Vector; + + +/** + * The Protocol class provides a set of common services for protocol layers. Each layer has to + * be a subclass of Protocol and override a number of methods (typically just up(), + * down() and getName(). Layers are stacked in a certain order to form + * a protocol stack. Events are passed from lower + * layers to upper ones and vice versa. E.g. a Message received by the UDP layer at the bottom + * will be passed to its higher layer as an Event. That layer will in turn pass the Event to + * its layer and so on, until a layer handles the Message and sends a response or discards it, + * the former resulting in another Event being passed down the stack. + *

        + * The important thing to bear in mind is that Events have to passed on between layers in FIFO + * order which is guaranteed by the Protocol implementation and must be guranteed by subclasses + * implementing their on Event queuing.

        + * Note that each class implementing interface Protocol MUST provide an empty, public + * constructor ! + * + * @author Bela Ban + * @version $Id: Protocol.java,v 1.1 2012/08/17 14:51:28 marcin Exp $ + */ +public abstract class Protocol { + protected final Properties props=new Properties(); + protected Protocol up_prot=null, down_prot=null; + protected ProtocolStack stack=null; + protected boolean stats=true; // determines whether to collect statistics (and expose them via JMX) + protected final Log log=LogFactory.getLog(this.getClass()); + + + /** + * Configures the protocol initially. A configuration string consists of name=value + * items, separated by a ';' (semicolon), e.g.:

        +     * "loopback=false;unicast_inport=4444"
        +     * 
        + */ + public boolean setProperties(Properties props) { + if(props != null) + this.props.putAll(props); + return true; + } + + + /** Called by Configurator. Removes 2 properties which are used by the Protocol directly and then + * calls setProperties(), which might invoke the setProperties() method of the actual protocol instance. + */ + public boolean setPropertiesInternal(Properties props) { + this.props.putAll(props); + + String str=props.getProperty("down_thread"); + if(str != null) { + if(log.isWarnEnabled()) + log.warn("down_thread was deprecated and is ignored"); + props.remove("down_thread"); + } + + str=props.getProperty("down_thread_prio"); + if(str != null) { + if(log.isWarnEnabled()) + log.warn("down_thread_prio was deprecated and is ignored"); + props.remove("down_thread_prio"); + } + + str=props.getProperty("up_thread"); + if(str != null) { + if(log.isWarnEnabled()) + log.warn("up_thread was deprecated and is ignored"); + props.remove("up_thread"); + } + + str=props.getProperty("up_thread_prio"); + if(str != null) { + if(log.isWarnEnabled()) + log.warn("up_thread_prio was deprecated and is ignored"); + props.remove("up_thread_prio"); + } + + str=props.getProperty("stats"); + if(str != null) { + stats=Boolean.valueOf(str).booleanValue(); + props.remove("stats"); + } + + return setProperties(props); + } + + + public Properties getProperties() { + return props; + } + + public ProtocolStack getProtocolStack(){ + return stack; + } + + protected TP getTransport() { + Protocol retval=this; + while(retval != null && retval.down_prot != null) { + retval=retval.down_prot; + } + return (TP)retval; + } + + /** Supposed to be overwritten by subclasses. Usually the transport returns a valid non-null thread factory, but + * thread factories can also be created by individual protocols + * @return + */ + public ThreadFactory getThreadFactory() { + return down_prot != null? down_prot.getThreadFactory(): null; + } + + + /** @deprecated up_thread was removed + * @return false by default + */ + public boolean upThreadEnabled() { + return false; + } + + /** + * @deprecated down thread was removed + * @return boolean False by default + */ + public boolean downThreadEnabled() { + return false; + } + + public boolean statsEnabled() { + return stats; + } + + public void enableStats(boolean flag) { + stats=flag; + } + + public void resetStats() { + ; + } + + public String printStats() { + return null; + } + + public Map dumpStats() { + return null; + } + + + /** + * Called after instance has been created (null constructor) and before protocol is started. + * Properties are already set. Other protocols are not yet connected and events cannot yet be sent. + * @exception Exception Thrown if protocol cannot be initialized successfully. This will cause the + * ProtocolStack to fail, so the channel constructor will throw an exception + */ + public void init() throws Exception { + } + + /** + * This method is called on a {@link org.jgroups.Channel#connect(String)}. Starts work. + * Protocols are connected and queues are ready to receive events. + * Will be called from bottom to top. This call will replace + * the START and START_OK events. + * @exception Exception Thrown if protocol cannot be started successfully. This will cause the ProtocolStack + * to fail, so {@link org.jgroups.Channel#connect(String)} will throw an exception + */ + public void start() throws Exception { + } + + /** + * This method is called on a {@link org.jgroups.Channel#disconnect()}. Stops work (e.g. by closing multicast socket). + * Will be called from top to bottom. This means that at the time of the method invocation the + * neighbor protocol below is still working. This method will replace the + * STOP, STOP_OK, CLEANUP and CLEANUP_OK events. The ProtocolStack guarantees that + * when this method is called all messages in the down queue will have been flushed + */ + public void stop() { + } + + + /** + * This method is called on a {@link org.jgroups.Channel#close()}. + * Does some cleanup; after the call the VM will terminate + */ + public void destroy() { + } + + + /** List of events that are required to be answered by some layer above. + @return Vector (of Integers) */ + public Vector requiredUpServices() { + return null; + } + + /** List of events that are required to be answered by some layer below. + @return Vector (of Integers) */ + public Vector requiredDownServices() { + return null; + } + + /** List of events that are provided to layers above (they will be handled when sent down from + above). + @return Vector (of Integers) */ + public Vector providedUpServices() { + return null; + } + + /** List of events that are provided to layers below (they will be handled when sent down from + below). + @return Vector providedDownServices() { + return null; + } + + + public abstract String getName(); // all protocol names have to be unique ! + + public Protocol getUpProtocol() { + return up_prot; + } + + public Protocol getDownProtocol() { + return down_prot; + } + + public void setUpProtocol(Protocol up_prot) { + this.up_prot=up_prot; + } + + public void setDownProtocol(Protocol down_prot) { + this.down_prot=down_prot; + } + + public void setProtocolStack(ProtocolStack stack) { + this.stack=stack; + } + + + /** + * An event was received from the layer below. Usually the current layer will want to examine + * the event type and - depending on its type - perform some computation + * (e.g. removing headers from a MSG event type, or updating the internal membership list + * when receiving a VIEW_CHANGE event). + * Finally the event is either a) discarded, or b) an event is sent down + * the stack using down_prot.down() or c) the event (or another event) is sent up + * the stack using up_prot.up(). + */ + public Object up(Event evt) { + return up_prot.up(evt); + } + + + /** + * An event is to be sent down the stack. The layer may want to examine its type and perform + * some action on it, depending on the event's type. If the event is a message MSG, then + * the layer may need to add a header to it (or do nothing at all) before sending it down + * the stack using down_prot.down(). In case of a GET_ADDRESS event (which tries to + * retrieve the stack's address from one of the bottom layers), the layer may need to send + * a new response event back up the stack using up_prot.up(). + */ + public Object down(Event evt) { + return down_prot.down(evt); + } + + + + +} Index: 3rdParty_sources/jgroups/org/jgroups/stack/ProtocolStack.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/stack/ProtocolStack.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/stack/ProtocolStack.java 17 Aug 2012 14:51:27 -0000 1.1 @@ -0,0 +1,482 @@ +package org.jgroups.stack; + +import org.jgroups.*; +import org.jgroups.conf.ClassConfigurator; +import org.jgroups.protocols.TP; +import org.jgroups.util.*; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.locks.ReentrantLock; + + +/** + * A ProtocolStack manages a number of protocols layered above each other. It creates all + * protocol classes, initializes them and, when ready, starts all of them, beginning with the + * bottom most protocol. It also dispatches messages received from the stack to registered + * objects (e.g. channel, GMP) and sends messages sent by those objects down the stack.

        + * The ProtocolStack makes use of the Configurator to setup and initialize stacks, and to + * destroy them again when not needed anymore + * @author Bela Ban + * @version $Id: ProtocolStack.java,v 1.1 2012/08/17 14:51:27 marcin Exp $ + */ +public class ProtocolStack extends Protocol implements Transport { + public static final int ABOVE = 1; // used by insertProtocol() + public static final int BELOW = 2; // used by insertProtocol() + + private Protocol top_prot = null; + private Protocol bottom_prot = null; + private String setup_string; + private JChannel channel = null; + private volatile boolean stopped = true; + + + /** Locks acquired by protocol below, need to get released on down(). + * See http://jira.jboss.com/jira/browse/JGRP-535 for details */ + private final Map locks=new ConcurrentHashMap(); + + + /** Holds the shared transports, keyed by 'TP.singleton_name'. + * The values are the transport and the use count for start() (decremented by stop() */ + private static final ConcurrentMap> singleton_transports=new ConcurrentHashMap>(); + + + + public ProtocolStack(JChannel channel, String setup_string) throws ChannelException { + this.setup_string=setup_string; + this.channel=channel; + ClassConfigurator.getInstance(true); // will create the singleton + } + + + + /** Only used by Simulator; don't use */ + public ProtocolStack() throws ChannelException { + this(null,null); + } + + /** + * @deprecated Use {@link org.jgroups.stack.Protocol#getThreadFactory()} instead + * @return + */ + public ThreadFactory getThreadFactory(){ + getTransport().getThreadFactory(); + TP transport=getTransport(); + return transport != null? transport.getThreadFactory() : null; + } + + @Deprecated + public static ThreadFactory getTimerThreadFactory() { + throw new UnsupportedOperationException("get the timer thread factory directly from the transport"); + } + + /** + * @deprecated Use {@link org.jgroups.stack.Protocol#getThreadFactory()} instead + * @param f + */ + public void setThreadFactory(ThreadFactory f) { + } + + /** + * @deprecated Use {@link TP#setTimerThreadFactory(org.jgroups.util.ThreadFactory)} instead + * @param f + */ + public static void setTimerThreadFactory(ThreadFactory f) { + } + + public Map getLocks() { + return locks; + } + + public Channel getChannel() { + return channel; + } + + + /** + * @deprecated Use {@link org.jgroups.protocols.TP#getTimer()} to fetch the timer and call getCorePoolSize() directly + * @return + */ + public int getTimerThreads() { + TP transport=getTransport(); + TimeScheduler timer; + if(transport != null) { + timer=transport.getTimer(); + if(timer != null) + return timer.getCorePoolSize(); + } + return -1; + } + + /** Returns all protocols in a list, from top to bottom. These are not copies of protocols, + so modifications will affect the actual instances ! */ + public Vector getProtocols() { + Protocol p; + Vector v=new Vector(); + + p=top_prot; + while(p != null) { + v.addElement(p); + p=p.getDownProtocol(); + } + return v; + } + + /** Returns the bottom most protocol */ + public TP getTransport() { + Vector prots=getProtocols(); + return (TP)(!prots.isEmpty()? prots.lastElement() : null); + } + + public static ConcurrentMap> getSingletonTransports() { + return singleton_transports; + } + + /** + * + * @return Map> + */ + public Map dumpStats() { + Protocol p; + Map retval=new HashMap(), tmp; + String prot_name; + + p=top_prot; + while(p != null) { + prot_name=p.getName(); + tmp=p.dumpStats(); + if(prot_name != null && tmp != null) + retval.put(prot_name, tmp); + p=p.getDownProtocol(); + } + return retval; + } + + public Map dumpStats(String protocol_name) { + Protocol p; + Map retval=new HashMap(), tmp; + String prot_name; + + p=top_prot; + while(p != null) { + prot_name=p.getName(); + if(prot_name.equals(protocol_name)) { + tmp=p.dumpStats(); + if(tmp != null) + retval.put(prot_name, tmp); + } + p=p.getDownProtocol(); + } + return retval; + } + + /** + * @deprecated Use {@link org.jgroups.protocols.TP#getTimer()} instead to fetch the timer from the + * transport and then invoke the method on it + * @return + */ + public String dumpTimerQueue() { + TP transport=getTransport(); + TimeScheduler timer; + if(transport != null) { + timer=transport.getTimer(); + if(timer != null) + return timer.dumpTaskQueue(); + } + return ""; + } + + /** + * Prints the names of the protocols, from the bottom to top. If include_properties is true, + * the properties for each protocol will also be printed. + */ + public String printProtocolSpec(boolean include_properties) { + StringBuilder sb=new StringBuilder(); + Protocol prot=top_prot; + Properties tmpProps; + String name; + Map.Entry entry; + + while(prot != null) { + name=prot.getName(); + if(name != null) { + if("ProtocolStack".equals(name)) + break; + sb.append(name); + if(include_properties) { + tmpProps=prot.getProperties(); + if(tmpProps != null) { + sb.append('\n'); + for(Iterator it=tmpProps.entrySet().iterator(); it.hasNext();) { + entry=(Map.Entry)it.next(); + sb.append(entry).append("\n"); + } + } + } + sb.append('\n'); + + prot=prot.getDownProtocol(); + } + } + + return sb.toString(); + } + + public String printProtocolSpecAsXML() { + StringBuilder sb=new StringBuilder(); + Protocol prot=bottom_prot; + Properties tmpProps; + String name; + Map.Entry entry; + int len, max_len=30; + + sb.append("\n"); + while(prot != null) { + name=prot.getName(); + if(name != null) { + if("ProtocolStack".equals(name)) + break; + sb.append(" <").append(name).append(" "); + tmpProps=prot.getProperties(); + if(tmpProps != null) { + len=name.length(); + String s; + for(Iterator it=tmpProps.entrySet().iterator(); it.hasNext();) { + entry=(Map.Entry)it.next(); + s=entry.getKey() + "=\"" + entry.getValue() + "\" "; + if(len + s.length() > max_len) { + sb.append("\n "); + len=8; + } + sb.append(s); + len+=s.length(); + } + } + sb.append("/>\n"); + prot=prot.getUpProtocol(); + } + } + sb.append(""); + + return sb.toString(); + } + + public String printProtocolSpecAsPlainString() { + StringBuilder sb=new StringBuilder(); + Protocol prot=bottom_prot; + Properties tmpProps; + boolean initialized=false; + Class clazz; + + while(prot != null) { + clazz=prot.getClass(); + if(clazz.equals(ProtocolStack.class)) + break; + if(initialized) + sb.append(":"); + else + initialized=true; + sb.append(clazz.getName()); + tmpProps=prot.getProperties(); + if(tmpProps != null && !tmpProps.isEmpty()) { + sb.append("("); + boolean first=true; + for(Map.Entry entry: tmpProps.entrySet()) { + if(first) + first=false; + else + sb.append(";"); + sb.append(entry.getKey() + "=" + entry.getValue()); + } + sb.append(")"); + } + sb.append("\n"); + prot=prot.getUpProtocol(); + } + return sb.toString(); + } + + + public void setup() throws Exception { + if(top_prot == null) { + top_prot=Configurator.setupProtocolStack(setup_string, this); + top_prot.setUpProtocol(this); + bottom_prot=Configurator.getBottommostProtocol(top_prot); + List protocols=getProtocols(); + Configurator.initProtocolStack(protocols); // calls init() on each protocol, from bottom to top + } + } + + + + + /** + * Creates a new protocol given the protocol specification. + * @param prot_spec The specification of the protocol. Same convention as for specifying a protocol stack. + * An exception will be thrown if the class cannot be created. Example: + *

        "VERIFY_SUSPECT(timeout=1500)"
        Note that no colons (:) have to be + * specified + * @return Protocol The newly created protocol + * @exception Exception Will be thrown when the new protocol cannot be created + */ + public Protocol createProtocol(String prot_spec) throws Exception { + return Configurator.createProtocol(prot_spec, this); + } + + + + + + + /** + * Inserts an already created (and initialized) protocol into the protocol list. Sets the links + * to the protocols above and below correctly and adjusts the linked list of protocols accordingly. + * Note that this method may change the value of top_prot or bottom_prot. + * @param prot The protocol to be inserted. Before insertion, a sanity check will ensure that none + * of the existing protocols have the same name as the new protocol. + * @param position Where to place the protocol with respect to the neighbor_prot (ABOVE, BELOW) + * @param neighbor_prot The name of the neighbor protocol. An exception will be thrown if this name + * is not found + * @exception Exception Will be thrown when the new protocol cannot be created, or inserted. + */ + public void insertProtocol(Protocol prot, int position, String neighbor_prot) throws Exception { + Configurator.insertProtocol(prot, position, neighbor_prot, this); + } + + + public void insertProtocol(Protocol prot, int position, Class neighbor_prot) throws Exception { + Configurator.insertProtocol(prot, position, neighbor_prot, this); + } + + + + /** + * Removes a protocol from the stack. Stops the protocol and readjusts the linked lists of + * protocols. + * @param prot_name The name of the protocol. Since all protocol names in a stack have to be unique + * (otherwise the stack won't be created), the name refers to just 1 protocol. + * @exception Exception Thrown if the protocol cannot be stopped correctly. + */ + public Protocol removeProtocol(String prot_name) throws Exception { + return Configurator.removeProtocol(top_prot, prot_name); + } + + + /** Returns a given protocol or null if not found */ + public Protocol findProtocol(String name) { + Protocol tmp=top_prot; + String prot_name; + while(tmp != null) { + prot_name=tmp.getName(); + if(prot_name != null && prot_name.equals(name)) + return tmp; + tmp=tmp.getDownProtocol(); + } + return null; + } + + + public Protocol findProtocol(Class clazz) { + Protocol tmp=top_prot; + while(tmp != null) { + Class protClass=tmp.getClass(); + if(clazz.isAssignableFrom(protClass)){ + return tmp; + } + tmp=tmp.getDownProtocol(); + } + return null; + } + + + + public void destroy() { + if(top_prot != null) { + Configurator.destroyProtocolStack(getProtocols()); // destroys msg queues and threads + top_prot=null; + } + } + + + + /** + * Start all layers. The {@link Protocol#start()} method is called in each protocol, + * from top to bottom. + * Each layer can perform some initialization, e.g. create a multicast socket + */ + public void startStack(String cluster_name) throws Exception { + if(stopped == false) return; + Configurator.startProtocolStack(getProtocols(), cluster_name, singleton_transports); + stopped=false; + } + + + + + /** + * Iterates through all the protocols from top to bottom and does the following: + *
          + *
        1. Waits until all messages in the down queue have been flushed (ie., size is 0) + *
        2. Calls stop() on the protocol + *
        + */ + public void stopStack(String cluster_name) { + if(stopped) return; + Configurator.stopProtocolStack(getProtocols(), cluster_name, singleton_transports); + stopped=true; + } + + /** + * Not needed anymore, just left in here for backwards compatibility with JBoss AS + * @deprecated + */ + public void flushEvents() { + + } + + + + /*--------------------------- Transport interface ------------------------------*/ + + public void send(Message msg) throws Exception { + down(new Event(Event.MSG, msg)); + } + + public Object receive(long timeout) throws Exception { + throw new Exception("ProtocolStack.receive(): not implemented !"); + } + /*------------------------- End of Transport interface ---------------------------*/ + + + + + + /*--------------------------- Protocol functionality ------------------------------*/ + public String getName() {return "ProtocolStack";} + + + + + public Object up(Event evt) { + return channel.up(evt); + } + + + + public Object down(Event evt) { + ReentrantLock lock=locks.remove(Thread.currentThread()); + if(lock != null && lock.isHeldByCurrentThread()) { + lock.unlock(); + if(log.isTraceEnabled()) + log.trace("released lock held by " + Thread.currentThread()); + } + if(top_prot != null) + return top_prot.down(evt); + return null; + } + + + + } + Index: 3rdParty_sources/jgroups/org/jgroups/stack/Retransmitter.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/stack/Retransmitter.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/stack/Retransmitter.java 17 Aug 2012 14:51:27 -0000 1.1 @@ -0,0 +1,252 @@ +// $Id: Retransmitter.java,v 1.1 2012/08/17 14:51:27 marcin Exp $ + +package org.jgroups.stack; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.Address; +import org.jgroups.util.TimeScheduler; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.Future; + + +/** + * Maintains a pool of sequence numbers of messages that need to be retransmitted. Messages + * are aged and retransmission requests sent according to age (configurable backoff). If a + * TimeScheduler instance is given to the constructor, it will be used, otherwise Reransmitter + * will create its own. The retransmit timeouts have to be set first thing after creating an instance. + * The add() method adds the sequence numbers of messages to be retransmitted. The + * remove() method removes a sequence number again, cancelling retransmission requests for it. + * Whenever a message needs to be retransmitted, the RetransmitCommand.retransmit() method is called. + * It can be used e.g. by an ack-based scheme (e.g. AckSenderWindow) to retransmit a message to the receiver, or + * by a nak-based scheme to send a retransmission request to the sender of the missing message.
        + * Changes Aug 2007 (bela): the retransmitter was completely rewritten. Entry was removed, instead all tasks + * are directly placed into a hashmap, keyed by seqnos. When a message has been received, we simply remove + * the task from the hashmap and cancel it. This simplifies the code and avoids having to iterate through + * the (previous) message list linearly on removal. Performance is about the same, or slightly better in + * informal tests. + * @author Bela Ban + * @version $Revision: 1.1 $ + */ +public class Retransmitter { + + private static final long SEC=1000; + /** Default retransmit intervals (ms) - exponential approx. */ + private Interval RETRANSMIT_TIMEOUTS=new StaticInterval(2 * SEC, 3 * SEC, 5 * SEC, 8 * SEC); + private Address sender=null; + private final ConcurrentMap msgs=new ConcurrentHashMap(11); + private RetransmitCommand cmd=null; + private boolean retransmitter_owned; + private TimeScheduler timer=null; + protected static final Log log=LogFactory.getLog(Retransmitter.class); + + + /** Retransmit command (see Gamma et al.) used to retrieve missing messages */ + public interface RetransmitCommand { + /** + * Get the missing messages between sequence numbers + * first_seqno and last_seqno. This can either be done by sending a + * retransmit message to destination sender (nak-based scheme), or by + * retransmitting the missing message(s) to sender (ack-based scheme). + * @param first_seqno The sequence number of the first missing message + * @param last_seqno The sequence number of the last missing message + * @param sender The destination of the member to which the retransmit request will be sent + * (nak-based scheme), or to which the message will be retransmitted (ack-based scheme). + */ + void retransmit(long first_seqno, long last_seqno, Address sender); + } + + + /** + * Create a new Retransmitter associated with the given sender address + * @param sender the address from which retransmissions are expected or to which retransmissions are sent + * @param cmd the retransmission callback reference + * @param sched retransmissions scheduler + */ + public Retransmitter(Address sender, RetransmitCommand cmd, TimeScheduler sched) { + init(sender, cmd, sched, false); + } + + + /** + * Create a new Retransmitter associated with the given sender address + * @param sender the address from which retransmissions are expected or to which retransmissions are sent + * @param cmd the retransmission callback reference + */ + public Retransmitter(Address sender, RetransmitCommand cmd) { + init(sender, cmd, new TimeScheduler(), true); + } + + + public void setRetransmitTimeouts(Interval interval) { + if(interval != null) + RETRANSMIT_TIMEOUTS=interval; + } + + + /** + * Add the given range [first_seqno, last_seqno] in the list of + * entries eligible for retransmission. If first_seqno > last_seqno, + * then the range [last_seqno, first_seqno] is added instead + *

        + * If retransmitter thread is suspended, wake it up + */ + public void add(long first_seqno, long last_seqno) { + if(first_seqno > last_seqno) { + long tmp=first_seqno; + first_seqno=last_seqno; + last_seqno=tmp; + } + + Task task; + for(long seqno=first_seqno; seqno <= last_seqno; seqno++) { + // each task needs its own retransmission interval, as they are stateful *and* mutable, so we *need* to copy ! + task=new Task(seqno, RETRANSMIT_TIMEOUTS.copy(), cmd, sender); + msgs.putIfAbsent(seqno, task); + task.doSchedule(timer); // Entry adds itself to the timer + } + + } + + /** + * Remove the given sequence number from the list of seqnos eligible + * for retransmission. If there are no more seqno intervals in the + * respective entry, cancel the entry from the retransmission + * scheduler and remove it from the pending entries + */ + public int remove(long seqno) { + Task task=msgs.remove(seqno); + if(task != null) { + task.cancel(); + return task.getNumRetransmits(); + } + return -1; + } + + /** + * Reset the retransmitter: clear all msgs and cancel all the + * respective tasks + */ + public void reset() { + for(Task task: msgs.values()) + task.cancel(); + msgs.clear(); + } + + /** + * Stop the rentransmition and clear all pending msgs. + *

        + * If this retransmitter has been provided an externally managed + * scheduler, then just clear all msgs and the associated tasks, else + * stop the scheduler. In this case the method blocks until the + * scheduler's thread is dead. Only the owner of the scheduler should + * stop it. + */ + public void stop() { + // i. If retransmitter is owned, stop it else cancel all tasks + // ii. Clear all pending msgs + if(retransmitter_owned) { + try { + timer.stop(); + } + catch(InterruptedException ex) { + if(log.isErrorEnabled()) log.error("failed stopping retransmitter", ex); + Thread.currentThread().interrupt(); // set interrupt flag again + } + } + else { + for(Task task: msgs.values()) + task.cancel(); + } + msgs.clear(); + } + + + public String toString() { + int size=size(); + StringBuilder sb=new StringBuilder(); + sb.append(size).append(" messages to retransmit: ").append(msgs.keySet()); + return sb.toString(); + } + + + public int size() { + return msgs.size(); + } + + + + + /* ------------------------------- Private Methods -------------------------------------- */ + + /** + * Init this object + * + * @param sender the address from which retransmissions are expected + * @param cmd the retransmission callback reference + * @param sched retransmissions scheduler + * @param sched_owned whether the scheduler parameter is owned by this + * object or is externally provided + */ + private void init(Address sender, RetransmitCommand cmd, TimeScheduler sched, boolean sched_owned) { + this.sender=sender; + this.cmd=cmd; + retransmitter_owned=sched_owned; + timer=sched; + } + + + /* ---------------------------- End of Private Methods ------------------------------------ */ + + + + /** + * The retransmit task executed by the scheduler in regular intervals + */ + private static class Task implements TimeScheduler.Task { + private final Interval intervals; + private long seqno=-1; + private Future future; + private Address sender=null; + protected int num_retransmits=0; + private RetransmitCommand command; + + protected Task(long seqno, Interval intervals, RetransmitCommand cmd, Address sender) { + this.seqno=seqno; + this.intervals=intervals; + this.command=cmd; + this.sender=sender; + } + + public int getNumRetransmits() { + return num_retransmits; + } + + public long nextInterval() { + return intervals.next(); + } + + public void doSchedule(TimeScheduler timer) { + future=timer.scheduleWithDynamicInterval(this); + } + + public void cancel() { + if(future != null) + future.cancel(false); + } + + public void run() { + command.retransmit(seqno, seqno, sender); + num_retransmits++; + } + + public String toString() { + return String.valueOf(seqno); + } + } + + +} + Index: 3rdParty_sources/jgroups/org/jgroups/stack/RouterStub.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/stack/RouterStub.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/stack/RouterStub.java 17 Aug 2012 14:51:28 -0000 1.1 @@ -0,0 +1,212 @@ +package org.jgroups.stack; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.Socket; +import java.net.SocketException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.Address; +import org.jgroups.util.Util; + +/** + * Client stub that talks to a remote GossipRouter + * + * @author Bela Ban + * @version $Id: RouterStub.java,v 1.1 2012/08/17 14:51:28 marcin Exp $ + */ +public class RouterStub { + + public final static int STATUS_CONNECTED = 0; + + public final static int STATUS_DISCONNECTED = 1; + + private String router_host = null; // name of the router host + + private int router_port = 0; // port on which router listens on + // router_host + + private Socket sock = null; // socket connecting to the router + + private DataOutputStream output = null; // output stream associated with + // sock + + private DataInputStream input = null; // input stream associated with sock + + private Address local_addr = null; // addr of group mbr. Once assigned, + // remains the same + + private volatile int connectionState = STATUS_DISCONNECTED; + + private static final Log log = LogFactory.getLog(RouterStub.class); + + private ConnectionListener conn_listener; + + private String groupname = null; + + private InetAddress bind_addr = null; + + private DatagramSocket my_sock = null; + + public interface ConnectionListener { + void connectionStatusChange(int state); + } + + /** + * Creates a stub for a remote Router object. + * + * @param routerHost + * The name of the router's host + * @param routerPort + * The router's port + */ + public RouterStub(String routerHost,int routerPort,InetAddress bindAddress){ + router_host = routerHost != null ? routerHost : "localhost"; + router_port = routerPort; + bind_addr = bindAddress; + } + + public boolean isConnected() { + return connectionState == STATUS_CONNECTED; + } + + public void setConnectionListener(ConnectionListener conn_listener) { + this.conn_listener = conn_listener; + } + + public synchronized Address getLocalAddress() throws SocketException { + if(local_addr == null){ + my_sock = new DatagramSocket(0, bind_addr); + local_addr = new IpAddress(bind_addr, my_sock.getLocalPort()); + } + return local_addr; + } + + /** + * Register this process with the router under groupname. + * + * @param groupname + * The name of the group under which to register + */ + public synchronized void connect(String groupname) throws Exception { + if(groupname == null || groupname.length() == 0) + throw new Exception("groupname is null"); + + if(!isConnected()){ + this.groupname = groupname; + try{ + sock = new Socket(router_host, router_port, bind_addr, 0); + sock.setSoLinger(true, 5); // 5 secs ! + output = new DataOutputStream(sock.getOutputStream()); + GossipData req = new GossipData(GossipRouter.CONNECT, + groupname, + getLocalAddress(), + null); + req.writeTo(output); + output.flush(); + input = new DataInputStream(sock.getInputStream()); + boolean connectedOk = input.readBoolean(); + if(connectedOk) + connectionStateChanged(STATUS_CONNECTED); + else + throw new Exception("Failed to get connection ack from gossip router"); + }catch(Exception e){ + if(log.isWarnEnabled()) + log.warn(this + " failed connecting to " + router_host + ":" + router_port); + Util.close(sock); + Util.close(input); + Util.close(output); + connectionStateChanged(STATUS_DISCONNECTED); + throw e; + } + } + } + + public synchronized void disconnect() { + try{ + GossipData req = new GossipData(GossipRouter.DISCONNECT, groupname, local_addr, null); + req.writeTo(output); + output.flush(); + }catch(Exception e){ + }finally{ + Util.close(output); + Util.close(input); + Util.close(sock); + Util.close(my_sock); + sock = null; + connectionStateChanged(STATUS_DISCONNECTED); + } + } + + public String toString() { + return "RouterStub[local_address=" + local_addr + + ",router_host=" + + router_host + + ",router_port=" + + router_port + + ",connected=" + + isConnected() + + "]"; + } + + public void sendToAllMembers(byte[] data, int offset, int length) throws Exception { + // null destination represents mcast + sendToSingleMember(null, data, offset, length); + } + + public synchronized void sendToSingleMember(Address dest, byte[] data, int offset, int length) throws Exception { + if(isConnected()){ + try{ + // 1. Group name + output.writeUTF(groupname); + + // 2. Destination address (null in case of mcast) + Util.writeAddress(dest, output); + + // 3. Length of byte buffer + output.writeInt(data.length); + + // 4. Byte buffer + output.write(data, 0, data.length); + + output.flush(); + + }catch(SocketException se){ + if(log.isWarnEnabled()) + log.warn("Router stub " + this + + " did not send message to " + + (dest == null ? "mcast" + : dest + " since underlying socket is closed")); + connectionStateChanged(STATUS_DISCONNECTED); + }catch(Exception e){ + if(log.isErrorEnabled()) + log.error("Router stub " + this + " failed sending message to router"); + connectionStateChanged(STATUS_DISCONNECTED); + throw new Exception("dest=" + dest + " (" + length + " bytes)", e); + } + } + } + + public DataInputStream getInputStream() throws IOException { + if(!isConnected()){ + throw new IOException("InputStream is closed"); + } + return input; + } + + private void connectionStateChanged(int newState) { + boolean notify = connectionState != newState; + connectionState = newState; + if(notify && conn_listener != null){ + try{ + conn_listener.connectionStatusChange(newState); + }catch(Throwable t){ + log.error("failed notifying ConnectionListener " + conn_listener, t); + } + } + } +} Index: 3rdParty_sources/jgroups/org/jgroups/stack/StateTransferInfo.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/stack/StateTransferInfo.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/stack/StateTransferInfo.java 17 Aug 2012 14:51:28 -0000 1.1 @@ -0,0 +1,92 @@ +// $Id: StateTransferInfo.java,v 1.1 2012/08/17 14:51:28 marcin Exp $ + +package org.jgroups.stack; + + +import java.io.InputStream; +import java.io.OutputStream; + +import org.jgroups.Address; + + +/** + * Contains parameters for state transfer. Exchanged between channel and STATE_TRANSFER + * layer. The state is retrieved from 'target'. If target is null, then the state will be retrieved from the oldest + * member (usually the coordinator). + * @author Bela Ban + * @version $Id: StateTransferInfo.java,v 1.1 2012/08/17 14:51:28 marcin Exp $ + */ +public class StateTransferInfo { + public Address target=null; + public long timeout=0; + public byte[] state=null; + public String state_id=null; + public InputStream inputStream = null; + public OutputStream outputStream = null; + + + + public StateTransferInfo() { + } + + public StateTransferInfo(Address target) { + this.target=target; + } + + public StateTransferInfo(Address target, long timeout) { + this.target=target; + this.timeout=timeout; + } + + public StateTransferInfo(Address target, String state_id, long timeout) { + this.target=target; + this.state_id=state_id; + this.timeout=timeout; + } + + public StateTransferInfo(Address target, String state_id, long timeout, byte[] state) { + this.target=target; + this.state=state; + this.state_id=state_id; + this.timeout=timeout; + } + + public StateTransferInfo(Address target, InputStream is, String state_id) { + this.target=target; + this.state_id=state_id; + this.inputStream=is; + } + + public StateTransferInfo(Address target, OutputStream os, String state_id) { + this.target=target; + this.state_id=state_id; + this.outputStream=os; + } + + + + + public StateTransferInfo copy() { + if(inputStream!=null){ + return new StateTransferInfo(target,inputStream,state_id); + } + else if(outputStream!=null){ + return new StateTransferInfo(target,outputStream,state_id); + } + else{ + return new StateTransferInfo(target, state_id, timeout, state); + } + } + + + public String toString() { + StringBuilder ret=new StringBuilder(); + ret.append("target=" + target); + if(state != null) + ret.append(", state=" + state.length + " bytes"); + if(state_id != null) + ret.append(", state_id=" + state_id); + ret.append(", timeout=" + timeout); + return ret.toString(); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/stack/StaticInterval.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/stack/StaticInterval.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/stack/StaticInterval.java 17 Aug 2012 14:51:27 -0000 1.1 @@ -0,0 +1,46 @@ + +package org.jgroups.stack; + +import org.jgroups.annotations.GuardedBy; + + +/** + * Manages retransmission timeouts. Always returns the next timeout, until the last timeout in the + * array is reached. Returns the last timeout from then on. Note that this class is immutable, + * so it shouldn't be shared between instances, as {@link #next()} will modify the state. + * @author John Giorgiadis + * @author Bela Ban + * @version $Id: StaticInterval.java,v 1.1 2012/08/17 14:51:27 marcin Exp $ + */ +public class StaticInterval implements Interval { + private int next=0; + private final long[] values; + + public StaticInterval(long ... vals) { + if (vals.length == 0) + throw new IllegalArgumentException("zero length array passed as argument"); + this.values=vals; + } + + public Interval copy() { + return new StaticInterval(values); + } + + /** @return the next interval */ + @GuardedBy("interval") + public long next() { + // we don't need to synchronize because this method won't be called concurrently; each entry has its own copy + // of StaticInterval + // synchronized(values) { + if (next >= values.length) + return(values[values.length-1]); + else + return(values[next++]); + // } + } + + +} + + + Index: 3rdParty_sources/jgroups/org/jgroups/stack/package.html =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/stack/package.html,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/stack/package.html 17 Aug 2012 14:51:28 -0000 1.1 @@ -0,0 +1,5 @@ + + + Support for managing protocol stacks. + + \ No newline at end of file Index: 3rdParty_sources/jgroups/org/jgroups/util/AckCollector.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/AckCollector.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/AckCollector.java 17 Aug 2012 14:51:02 -0000 1.1 @@ -0,0 +1,107 @@ +package org.jgroups.util; + +import org.jgroups.Address; +import org.jgroups.TimeoutException; +import org.jgroups.View; +import org.jgroups.ViewId; + +import java.util.*; + +/** + * @author Bela Ban + * @version $Id: AckCollector.java,v 1.1 2012/08/17 14:51:02 marcin Exp $ + */ +public class AckCollector { + /** List: list of members from whom we haven't received an ACK yet */ + private final List missing_acks; + private final Set received_acks=new HashSet(); + private final Promise all_acks_received=new Promise(); + private final Set
        suspected_mbrs=new HashSet
        (); + + + public AckCollector() { + missing_acks=new ArrayList(); + } + + public AckCollector(ViewId v, List l) { + missing_acks=new ArrayList(l); + } + + public String printMissing() { + synchronized(this) { + return missing_acks.toString(); + } + } + + public String printReceived() { + synchronized(this) { + return received_acks.toString(); + } + } + + public void reset(ViewId v, List
        members) { + synchronized(this) { + suspected_mbrs.clear(); + missing_acks.clear(); + received_acks.clear(); + if(members != null && !members.isEmpty()) + missing_acks.addAll(members); + missing_acks.removeAll(suspected_mbrs); + all_acks_received.reset(); + } + } + + public int size() { + synchronized(this) { + return missing_acks.size(); + } + } + + public void ack(Object member) { + synchronized(this) { + missing_acks.remove(member); + received_acks.add(member); + if(missing_acks.isEmpty()) + all_acks_received.setResult(Boolean.TRUE); + } + } + + public void suspect(Address member) { + synchronized(this) { + ack(member); + suspected_mbrs.add(member); + } + } + + public void unsuspect(Address member) { + synchronized(this) { + suspected_mbrs.remove(member); + } + } + + public void handleView(View v) { + if(v == null) return; + Vector
        mbrs=v.getMembers(); + synchronized(this) { + suspected_mbrs.retainAll(mbrs); + } + } + + public boolean waitForAllAcks() { + if(missing_acks.isEmpty()) + return true; + Object result=all_acks_received.getResult(); + return result != null && result instanceof Boolean && ((Boolean)result).booleanValue(); + } + + public boolean waitForAllAcks(long timeout) throws TimeoutException { + if(missing_acks.isEmpty()) + return true; + Object result=all_acks_received.getResultWithTimeout(timeout); + return result != null && result instanceof Boolean && ((Boolean)result).booleanValue(); + } + + public String toString() { + return "missing=" + printMissing() + ", received=" + printReceived(); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/util/BoundedList.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/BoundedList.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/BoundedList.java 17 Aug 2012 14:51:01 -0000 1.1 @@ -0,0 +1,45 @@ +package org.jgroups.util; + +import java.util.concurrent.ConcurrentLinkedQueue; + + +/** + * A bounded subclass of LinkedList, oldest elements are removed once max capacity is exceeded. Note that this + * class is not synchronized (like LinkedList). + * @author Bela Ban Nov 20, 2003 + * @version $Id: BoundedList.java,v 1.1 2012/08/17 14:51:01 marcin Exp $ + */ +public class BoundedList extends ConcurrentLinkedQueue { + int max_capacity=10; + + + public BoundedList() { + super(); + } + + public BoundedList(int size) { + super(); + max_capacity=size; + } + + + /** + * Adds an element at the tail. Removes an object from the head if capacity is exceeded + * @param obj The object to be added + */ + public boolean add(T obj) { + if(obj == null) return false; + while(size() >= max_capacity && size() > 0) { + poll(); + } + return super.add(obj); + } + + + + public T removeFromHead() { + return poll(); + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/util/Buffer.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/Buffer.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/Buffer.java 17 Aug 2012 14:51:01 -0000 1.1 @@ -0,0 +1,48 @@ +package org.jgroups.util; + +/** + * Buffer with an offset and length. Will be replaced with NIO equivalent once JDK 1.4 becomes baseline. This class is + * immutable + * @author Bela Ban + * @version $Id: Buffer.java,v 1.1 2012/08/17 14:51:01 marcin Exp $ + */ +public class Buffer { + private final byte[] buf; + private final int offset; + private final int length; + + public Buffer(byte[] buf, int offset, int length) { + this.buf=buf; + this.offset=offset; + this.length=length; + } + + public byte[] getBuf() { + return buf; + } + + public int getOffset() { + return offset; + } + + public int getLength() { + return length; + } + + public Buffer copy() { + byte[] new_buf=buf != null? new byte[length] : null; + int new_length=new_buf != null? new_buf.length : 0; + if(new_buf != null) + System.arraycopy(buf, offset, new_buf, 0, length); + return new Buffer(new_buf, 0, new_length); + } + + public String toString() { + StringBuilder sb=new StringBuilder(); + sb.append(length).append(" bytes"); + if(offset > 0) + sb.append(" (offset=").append(offset).append(")"); + return sb.toString(); + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/util/Command.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/Command.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/Command.java 17 Aug 2012 14:51:02 -0000 1.1 @@ -0,0 +1,12 @@ +// $Id: Command.java,v 1.1 2012/08/17 14:51:02 marcin Exp $ + +package org.jgroups.util; + +/** + * The Command patttern (see Gamma et al.). Implementations would provide their + * own execute method. + * @author Bela Ban + */ +public interface Command { + boolean execute() throws Exception; +} Index: 3rdParty_sources/jgroups/org/jgroups/util/ContextObjectInputStream.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/ContextObjectInputStream.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/ContextObjectInputStream.java 17 Aug 2012 14:51:01 -0000 1.1 @@ -0,0 +1,82 @@ +package org.jgroups.util; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectStreamClass; +import java.util.HashMap; + +/** + * ObjectInputStream which sets a contact classloader for reading bytes into objects. Copied from + * MarshalledValueInputStream of JBoss + * @author Bela Ban + * @version $Id: ContextObjectInputStream.java,v 1.1 2012/08/17 14:51:01 marcin Exp $ + */ +public class ContextObjectInputStream extends ObjectInputStream { + + + /** + * A class wide cache of proxy classes populated by resolveProxyClass + */ + private static final HashMap classCache=new HashMap(); + + private static final HashMap primClasses = new HashMap(9, 1.0F); + static { + primClasses.put("boolean", boolean.class); + primClasses.put("byte", byte.class); + primClasses.put("char", char.class); + primClasses.put("short", short.class); + primClasses.put("int", int.class); + primClasses.put("long", long.class); + primClasses.put("float", float.class); + primClasses.put("double", double.class); + primClasses.put("void", void.class); + } + + /** + * Creates a new instance of MarshalledValueOutputStream + */ + public ContextObjectInputStream(InputStream is) throws IOException { + super(is); + } + + + protected Class resolveClass(ObjectStreamClass v) throws IOException, ClassNotFoundException { + String className=v.getName(); + Class resolvedClass=null; + // Check the class cache first if it exists + synchronized(classCache) { + resolvedClass=(Class)classCache.get(className); + } + + if(resolvedClass == null) { + try { + resolvedClass=Util.loadClass(className, this.getClass()); + } + catch(ClassNotFoundException e) { + + } + if(resolvedClass == null) { + /* This is a backport von JDK 1.4's java.io.ObjectInputstream to support + * retrieval of primitive classes (e.g. Boolean.TYPE) in JDK 1.3. + * This is required for org.jgroups.blocks.MethodCall to support primitive + * Argument types in JDK1.3: + */ + resolvedClass = (Class) primClasses.get(className); + if (resolvedClass == null) { + + /* Use the super.resolveClass() call which will resolve array + classes and primitives. We do not use this by default as this can + result in caching of stale values across redeployments. + */ + resolvedClass=super.resolveClass(v); + } + } + synchronized(classCache) { + classCache.put(className, resolvedClass); + } + } + return resolvedClass; + } +} + Index: 3rdParty_sources/jgroups/org/jgroups/util/DefaultThreadFactory.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/DefaultThreadFactory.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/DefaultThreadFactory.java 17 Aug 2012 14:51:01 -0000 1.1 @@ -0,0 +1,136 @@ +package org.jgroups.util; + +/** + * Thread factory mainly responsible for naming of threads. Can be replaced by + * user. If use_numbering is set, a thread THREAD will be called THREAD-1, + * THREAD-2, and so on.

        If a pattern has been set (through setPattern()), + * then the cluster name and local address will also be added, e.g. + * THREAD-5,MyCluster,192.168.1.5:63754 or THREAD,MyCluster,192.168.1.5:63754 + * + * @author Vladimir Blagojevic + * @author Bela Ban + * @version $Id: DefaultThreadFactory.java,v 1.3.2.4 2008/05/26 09:14:38 belaban + * Exp $ + */ +public class DefaultThreadFactory implements ThreadFactory, ThreadManager { + protected final ThreadGroup group; + protected final String baseName; + protected final boolean createDaemons; + protected short counter=0; // if numbering is enabled + protected final boolean use_numbering; + + protected boolean includeClusterName=false; + protected boolean includeLocalAddress=false; + protected String clusterName=null; + protected String address=null; + protected ThreadDecorator threadDecorator=null; + + public DefaultThreadFactory(ThreadGroup group,String baseName,boolean createDaemons) { + this(group, baseName, createDaemons, false); + } + + public DefaultThreadFactory(ThreadGroup group, + String baseName, + boolean createDaemons, + boolean use_numbering) { + this.group=group; + this.baseName=baseName; + this.createDaemons=createDaemons; + this.use_numbering=use_numbering; + } + + public void setPattern(String pattern) { + if(pattern != null) { + includeClusterName=pattern.contains("c"); + includeLocalAddress=pattern.contains("l"); + } + } + + public void setIncludeClusterName(boolean includeClusterName) { + this.includeClusterName=includeClusterName; + } + + public void setClusterName(String channelName) { + clusterName=channelName; + } + + public void setAddress(String address) { + this.address=address; + } + + public ThreadDecorator getThreadDecorator() { + return threadDecorator; + } + + public void setThreadDecorator(ThreadDecorator threadDecorator) { + this.threadDecorator=threadDecorator; + } + + public Thread newThread(Runnable r, String name) { + return newThread(group, r, name); + } + + public Thread newThread(Runnable r) { + return newThread(group, r, baseName); + } + + public Thread newThread(ThreadGroup group, Runnable r, String name) { + return newThread(group, r, name, null, null); + } + + protected Thread newThread(ThreadGroup group, + Runnable r, + String name, + String address, + String cluster_name) { + Thread retval=new Thread(group, r, name); + retval.setDaemon(createDaemons); + renameThread(retval, address, cluster_name); + if(threadDecorator != null) + threadDecorator.threadCreated(retval); + return retval; + } + + public void renameThread(String base_name, Thread thread) { + renameThread(base_name, thread, address, clusterName); + } + + public void renameThread(String base_name, Thread thread, String address, String cluster_name) { + if(thread == null) + return; + StringBuilder sb=new StringBuilder(base_name != null? base_name : thread.getName()); + if(use_numbering) { + short id; + synchronized(this) { + id=++counter; + } + sb.append("-" + id); + } + if(includeClusterName) { + sb.append(','); + if(cluster_name != null) + sb.append(cluster_name); + else + sb.append(this.clusterName); + } + + if(includeLocalAddress) { + sb.append(','); + if(address != null) + sb.append(address); + else + sb.append(this.address); + } + + if(use_numbering || includeClusterName || includeLocalAddress) + thread.setName(sb.toString()); + } + + protected void renameThread(Thread thread, String address, String cluster_name) { + renameThread(null, thread, address, cluster_name); + } + + public void renameThread(Thread thread) { + renameThread(null, thread); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/util/Digest.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/Digest.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/Digest.java 17 Aug 2012 14:51:01 -0000 1.1 @@ -0,0 +1,498 @@ +package org.jgroups.util; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.Address; +import org.jgroups.Global; +import org.jgroups.annotations.Immutable; + +import java.io.*; +import static java.lang.Math.max; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + + +/** + * A message digest, which is used by the PBCAST layer for gossiping (also used by NAKACK for + * keeping track of current seqnos for all members). It contains pairs of senders and a range of seqnos + * (low and high), where each sender is associated with its highest and lowest seqnos seen so far. That + * is, the lowest seqno which was not yet garbage-collected and the highest that was seen so far and is + * deliverable (or was already delivered) to the application. A range of [0 - 0] means no messages have + * been received yet. + *

        April 3 2001 (bela): Added high_seqnos_seen member. It is used to disseminate + * information about the last (highest) message M received from a sender P. Since we might be using a + * negative acknowledgment message numbering scheme, we would never know if the last message was + * lost. Therefore we periodically gossip and include the last message seqno. Members who haven't seen + * it (e.g. because msg was dropped) will request a retransmission. See DESIGN for details. + * @author Bela Ban + * @version $Id: Digest.java,v 1.1 2012/08/17 14:51:01 marcin Exp $ + */ +public class Digest implements Externalizable, Streamable { + + public static final Digest EMPTY_DIGEST = new Digest(); + /** Map<Address, Entry> */ + protected final Map senders; + + protected static final Log log=LogFactory.getLog(Digest.class); + + + + /** Used for externalization */ + public Digest() { + senders=createSenders(7); + } + + public Digest(int size) { + senders=createSenders(size); + } + + /** Creates a new digest from an existing map by copying the keys and values from map */ + public Digest(Map map) { + senders=createSenders(map); + } + + + public Digest(Digest d) { + this(d.senders); + } + + + public Digest(Address sender, long low, long highest_delivered, long highest_received) { + senders=createSenders(1); + senders.put(sender, new Entry(low, highest_delivered, highest_received)); + } + + public Digest(Address sender, long low, long highest_delivered) { + senders=createSenders(1); + senders.put(sender, new Entry(low, highest_delivered)); + } + + /** Returns an unmodifiable map, so modifications will result in exceptions */ + public Map getSenders() { + return Collections.unmodifiableMap(senders); + } + + public boolean equals(Object obj) { + if(!(obj instanceof Digest)) + return false; + Digest other=(Digest)obj; + return senders.equals(other.senders); + } + + + public boolean contains(Address sender) { + return senders.containsKey(sender); + } + + /** Returns the Entry for the given sender. Note that Entry is immutable */ + public Entry get(Address sender) { + return senders.get(sender); + } + + + /** + * Compares two digests and returns true if the senders are the same, otherwise false. + * @param other + * @return True if senders are the same, otherwise false. + */ + public boolean sameSenders(Digest other) { + if(other == null) return false; + if(this.senders.size() != other.senders.size()) return false; + + Set

        my_senders=senders.keySet(), other_senders=other.senders.keySet(); + return my_senders.equals(other_senders); + } + + public Digest difference(Digest other) { + if(other == null) return copy(); + + Digest result=EMPTY_DIGEST; + if(this.equals(other)) { + return result; + } + else { + //find intersection and compare their entries + Map resultMap=new ConcurrentHashMap(7); + Set
        intersection=new TreeSet
        (this.senders.keySet()); + intersection.retainAll(other.senders.keySet()); + + for(Address address : intersection) { + Entry e1=this.get(address); + Entry e2=other.get(address); + if(e1.getHighestDeliveredSeqno() != e2.getHighestDeliveredSeqno()) { + long low=Math.min(e1.highest_delivered_seqno, e2.highest_delivered_seqno); + long high=max(e1.highest_delivered_seqno, e2.highest_delivered_seqno); + Entry r=new Entry(low, high); + resultMap.put(address, r); + } + } + + //any entries left in (this - intersection)? + //if yes, add them to result + if(intersection.size() != this.senders.keySet().size()) { + Set
        thisMinusInteresection=new TreeSet
        (this.senders.keySet()); + thisMinusInteresection.removeAll(intersection); + for(Address address : thisMinusInteresection) { + resultMap.put(address, new Entry(this.get(address))); + } + } + + //any entries left in (other - intersection)? + //if yes, add them to result + if(intersection.size() != other.senders.keySet().size()) { + Set
        otherMinusInteresection=new TreeSet
        (other.senders.keySet()); + otherMinusInteresection.removeAll(intersection); + for(Address address : otherMinusInteresection) { + resultMap.put(address, new Entry(other.get(address))); + } + } + result=new Digest(resultMap); + } + return result; + } + + public Digest highestSequence(Digest other) { + if(other == null) return copy(); + + Digest result=EMPTY_DIGEST; + if(this.equals(other)) { + return this; + } + else { + //find intersection and compare their entries + Map resultMap=new ConcurrentHashMap(7); + Set
        intersection=new TreeSet
        (this.senders.keySet()); + intersection.retainAll(other.senders.keySet()); + + for(Address address : intersection) { + Entry e1=this.get(address); + Entry e2=other.get(address); + + long high=max(e1.highest_delivered_seqno, e2.highest_delivered_seqno); + Entry r=new Entry(0, high); + resultMap.put(address, r); + } + + //any entries left in (this - intersection)? + //if yes, add them to result + if(intersection.size() != this.senders.keySet().size()) { + Set
        thisMinusInteresection=new TreeSet
        (this.senders.keySet()); + thisMinusInteresection.removeAll(intersection); + for(Address address : thisMinusInteresection) { + resultMap.put(address, new Entry(this.get(address))); + } + } + + //any entries left in (other - intersection)? + //if yes, add them to result + if(intersection.size() != other.senders.keySet().size()) { + Set
        otherMinusInteresection=new TreeSet
        (other.senders.keySet()); + otherMinusInteresection.removeAll(intersection); + for(Address address : otherMinusInteresection) { + resultMap.put(address, new Entry(other.get(address))); + } + } + result=new Digest(resultMap); + } + return result; + } + + + public int size() { + return senders.size(); + } + + + public long lowSeqnoAt(Address sender) { + Entry entry=senders.get(sender); + if(entry == null) + return -1; + else + return entry.low_seqno; + } + + + public long highestDeliveredSeqnoAt(Address sender) { + Entry entry=senders.get(sender); + if(entry == null) + return -1; + else + return entry.highest_delivered_seqno; + } + + + public long highestReceivedSeqnoAt(Address sender) { + Entry entry=senders.get(sender); + if(entry == null) + return -1; + else + return entry.highest_received_seqno; + } + + + /** + * Returns true if all senders of the current digest have their seqnos >= the ones from other + * @param other + * @return + */ + public boolean isGreaterThanOrEqual(Digest other) { + if(other == null) + return true; + Map our_map=getSenders(); + Address sender; + Entry my_entry, their_entry; + long my_highest, their_highest; + for(Map.Entry entry: our_map.entrySet()) { + sender=entry.getKey(); + my_entry=entry.getValue(); + their_entry=other.get(sender); + if(their_entry == null) + continue; + my_highest=my_entry.getHighest(); + their_highest=their_entry.getHighest(); + if(my_highest < their_highest) + return false; + } + return true; + } + + + public Digest copy() { + return new Digest(senders); + } + + + public String toString() { + StringBuilder sb=new StringBuilder(); + boolean first=true; + if(senders.isEmpty()) return "[]"; + Map.Entry entry; + Address key; + Entry val; + + for(Iterator> it=senders.entrySet().iterator(); it.hasNext();) { + entry=it.next(); + key=entry.getKey(); + val=entry.getValue(); + if(!first) { + sb.append(", "); + } + else { + first=false; + } + sb.append(key).append(": ").append('[').append(val.low_seqno).append(" : "); + sb.append(val.highest_delivered_seqno); + if(val.highest_received_seqno >= 0) + sb.append(" (").append(val.highest_received_seqno).append(")"); + sb.append("]"); + } + return sb.toString(); + } + + + public String printHighestDeliveredSeqnos() { + StringBuilder sb=new StringBuilder("["); + boolean first=true; + Map.Entry entry; + Address key; + Entry val; + + TreeMap copy=new TreeMap(senders); + for(Iterator> it=copy.entrySet().iterator(); it.hasNext();) { + entry=it.next(); + key=entry.getKey(); + val=entry.getValue(); + if(!first) { + sb.append(", "); + } + else { + first=false; + } + sb.append(key).append("#").append(val.highest_delivered_seqno); + } + sb.append(']'); + return sb.toString(); + } + + + public String printHighestReceivedSeqnos() { + StringBuilder sb=new StringBuilder(); + boolean first=true; + Map.Entry entry; + Address key; + Entry val; + + for(Iterator> it=senders.entrySet().iterator(); it.hasNext();) { + entry=it.next(); + key=entry.getKey(); + val=entry.getValue(); + if(!first) { + sb.append(", "); + } + else { + sb.append('['); + first=false; + } + sb.append(key).append("#").append(val.highest_received_seqno); + } + sb.append(']'); + return sb.toString(); + } + + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeObject(senders); + } + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + Map tmp=(Map)in.readObject(); + senders.clear(); + senders.putAll(tmp); + } + + public void writeTo(DataOutputStream out) throws IOException { + out.writeShort(senders.size()); + Map.Entry entry; + Address key; + Entry val; + for(Iterator> it=senders.entrySet().iterator(); it.hasNext();) { + entry=it.next(); + key=entry.getKey(); + val=entry.getValue(); + Util.writeAddress(key, out); + out.writeLong(val.low_seqno); + out.writeLong(val.highest_delivered_seqno); + out.writeLong(val.highest_received_seqno); + } + } + + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + short size=in.readShort(); + Map tmp=new HashMap(size); + Address key; + for(int i=0; i < size; i++) { + key=Util.readAddress(in); + tmp.put(key, new Entry(in.readLong(), in.readLong(), in.readLong())); + } + senders.clear(); + senders.putAll(tmp); + } + + + public long serializedSize() { + long retval=Global.SHORT_SIZE; // number of elements in 'senders' + if(!senders.isEmpty()) { + Address addr=senders.keySet().iterator().next(); + int len=addr.size() + + 2 * Global.BYTE_SIZE; // presence byte, IpAddress vs other address + len+=Entry.SIZE; // 3 longs in one Entry + retval+=len * senders.size(); + } + return retval; + } + + private static Map createSenders(int size) { + return new ConcurrentHashMap(size); + } + + private static Map createSenders(Map map) { + return new ConcurrentHashMap(map); + } + + + + /** + * Class keeping track of the lowest and highest sequence numbers delivered, and the highest + * sequence numbers received, per member. This class is immutable + */ + @Immutable + public static class Entry implements Externalizable, Streamable { + private long low_seqno=0; + private long highest_delivered_seqno=0; // the highest delivered seqno, e.g. in 1,2,4,5,7 --> 2 + private long highest_received_seqno=0; //the highest received seqno, e.g. in 1,2,4,5,7 --> 7 + final static int SIZE=Global.LONG_SIZE * 3; + + public Entry() { + } + + public Entry(long low_seqno, long highest_delivered_seqno, long highest_received_seqno) { + this.low_seqno=low_seqno; + this.highest_delivered_seqno=highest_delivered_seqno; + this.highest_received_seqno=highest_received_seqno; + check(); + } + + + + public Entry(long low_seqno, long highest_delivered_seqno) { + this.low_seqno=low_seqno; + this.highest_delivered_seqno=highest_delivered_seqno; + check(); + } + + public Entry(Entry other) { + if(other != null) { + low_seqno=other.low_seqno; + highest_delivered_seqno=other.highest_delivered_seqno; + highest_received_seqno=other.highest_received_seqno; + check(); + } + } + + public final long getLow() {return low_seqno;} + public final long getHighestDeliveredSeqno() {return highest_delivered_seqno;} + public final long getHighestReceivedSeqno() {return highest_received_seqno;} + + /** Return the max of the highest delivered or highest received seqno */ + public final long getHighest() {return max(highest_delivered_seqno, highest_received_seqno);} + + public boolean equals(Object obj) { + if(!(obj instanceof Entry)) + return false; + Entry other=(Entry)obj; + return low_seqno == other.low_seqno && highest_delivered_seqno == other.highest_delivered_seqno && highest_received_seqno == other.highest_received_seqno; + } + + public String toString() { + return new StringBuilder("low=").append(low_seqno).append(", highest delivered=").append(highest_delivered_seqno). + append(", highest received=").append(highest_received_seqno).toString(); + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeLong(low_seqno); + out.writeLong(highest_delivered_seqno); + out.writeLong(highest_received_seqno); + } + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + low_seqno=in.readLong(); + highest_delivered_seqno=in.readLong(); + highest_received_seqno=in.readLong(); + } + + public int size() { + return SIZE; + } + + public void writeTo(DataOutputStream out) throws IOException { + out.writeLong(low_seqno); + out.writeLong(highest_delivered_seqno); + out.writeLong(highest_received_seqno); + } + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + low_seqno=in.readLong(); + highest_delivered_seqno=in.readLong(); + highest_received_seqno=in.readLong(); + } + + + private void check() { + if(low_seqno > highest_delivered_seqno) + throw new IllegalArgumentException("low_seqno (" + low_seqno + ") is greater than highest_delivered_seqno (" + highest_delivered_seqno + ")"); + } + + + } +} Index: 3rdParty_sources/jgroups/org/jgroups/util/DirectExecutor.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/DirectExecutor.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/DirectExecutor.java 17 Aug 2012 14:51:01 -0000 1.1 @@ -0,0 +1,13 @@ +package org.jgroups.util; + +import java.util.concurrent.Executor; + +/** + * @author Bela Ban + * @version $Id: DirectExecutor.java,v 1.1 2012/08/17 14:51:01 marcin Exp $ + */ +public class DirectExecutor implements Executor { + public void execute(Runnable command) { + command.run(); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/util/ExposedBufferedInputStream.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/ExposedBufferedInputStream.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/ExposedBufferedInputStream.java 17 Aug 2012 14:51:01 -0000 1.1 @@ -0,0 +1,57 @@ +package org.jgroups.util; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.BufferedInputStream; +import java.io.InputStream; + +/** + * @author Bela Ban + * @version $Id: ExposedBufferedInputStream.java,v 1.1 2012/08/17 14:51:01 marcin Exp $ + */ +public class ExposedBufferedInputStream extends BufferedInputStream { + private final static Log log=LogFactory.getLog(ExposedBufferedInputStream.class); + /** + * Creates a BufferedInputStream + * and saves its argument, the input stream + * in, for later use. An internal + * buffer array is created and stored in buf. + * + * @param in the underlying input stream. + */ + public ExposedBufferedInputStream(InputStream in) { + super(in); + } + + /** + * Creates a BufferedInputStream + * with the specified buffer size, + * and saves its argument, the input stream + * in, for later use. An internal + * buffer array of length size + * is created and stored in buf. + * + * @param in the underlying input stream. + * @param size the buffer size. + * @throws IllegalArgumentException if size <= 0. + */ + public ExposedBufferedInputStream(InputStream in, int size) { + super(in, size); + } + + public void reset(int size) { + count=pos=marklimit=0; + markpos=-1; + if(buf != null) { + if(size > buf.length) { + buf=new byte[size]; + } + } + else { + buf=new byte[4096]; + if(log.isWarnEnabled()) + log.warn("output stream was closed, re-creating it (please don't close it)"); + } + } +} Index: 3rdParty_sources/jgroups/org/jgroups/util/ExposedBufferedOutputStream.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/ExposedBufferedOutputStream.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/ExposedBufferedOutputStream.java 17 Aug 2012 14:51:02 -0000 1.1 @@ -0,0 +1,40 @@ +package org.jgroups.util; + +import java.io.BufferedOutputStream; +import java.io.OutputStream; + +/** + * @author Bela Ban + * @version $Id: ExposedBufferedOutputStream.java,v 1.1 2012/08/17 14:51:02 marcin Exp $ + */ +public class ExposedBufferedOutputStream extends BufferedOutputStream { + /** + * Creates a new buffered output stream to write data to the + * specified underlying output stream. + * + * @param out the underlying output stream. + */ + public ExposedBufferedOutputStream(OutputStream out) { + super(out); + } + + /** + * Creates a new buffered output stream to write data to the + * specified underlying output stream with the specified buffer + * size. + * + * @param out the underlying output stream. + * @param size the buffer size. + * @throws IllegalArgumentException if size <= 0. + */ + public ExposedBufferedOutputStream(OutputStream out, int size) { + super(out, size); + } + + public void reset(int size) { + count=0; + if(size > buf.length) { + buf=new byte[size]; + } + } +} Index: 3rdParty_sources/jgroups/org/jgroups/util/ExposedByteArrayInputStream.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/ExposedByteArrayInputStream.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/ExposedByteArrayInputStream.java 17 Aug 2012 14:51:01 -0000 1.1 @@ -0,0 +1,98 @@ +package org.jgroups.util; + +import java.io.ByteArrayInputStream; + +/** + * @author Bela Ban + * @version $Id: ExposedByteArrayInputStream.java,v 1.1 2012/08/17 14:51:01 marcin Exp $ + */ +public class ExposedByteArrayInputStream extends ByteArrayInputStream { + + + /** + * Creates a ByteArrayInputStream + * so that it uses buf as its + * buffer array. + * The buffer array is not copied. + * The initial value of pos + * is 0 and the initial value + * of count is the length of + * buf. + * @param buf the input buffer. + */ + public ExposedByteArrayInputStream(byte[] buf) { + super(buf); + } + + /** + * Creates ByteArrayInputStream + * that uses buf as its + * buffer array. The initial value of pos + * is offset and the initial value + * of count is the minimum of offset+length + * and buf.length. + * The buffer array is not copied. The buffer's mark is + * set to the specified offset. + * @param buf the input buffer. + * @param offset the offset in the buffer of the first byte to read. + * @param length the maximum number of bytes to read from the buffer. + */ + public ExposedByteArrayInputStream(byte[] buf, int offset, int length) { + super(buf, offset, length); + } + + public void setData(byte[] buf, int offset, int length) { + this.buf=buf; + this.pos=offset; + this.count=Math.min(offset + length, buf.length); + this.mark=offset; + } + + + public int read() { + return (pos < count)? (buf[pos++] & 0xff) : -1; + } + + public int read(byte b[], int off, int len) { + if(b == null) { + throw new NullPointerException(); + } + else if(off < 0 || len < 0 || len > b.length - off) { + throw new IndexOutOfBoundsException(); + } + if(pos >= count) { + return -1; + } + if(pos + len > count) { + len=count - pos; + } + if(len <= 0) { + return 0; + } + System.arraycopy(buf, pos, b, off, len); + pos+=len; + return len; + } + + + public long skip(long n) { + if(pos + n > count) { + n=count - pos; + } + if(n < 0) { + return 0; + } + pos+=n; + return n; + } + + public int available() { + return count - pos; + } + + public void reset() { + pos=mark; + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/util/ExposedByteArrayOutputStream.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/ExposedByteArrayOutputStream.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/ExposedByteArrayOutputStream.java 17 Aug 2012 14:51:01 -0000 1.1 @@ -0,0 +1,192 @@ +package org.jgroups.util; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.util.Arrays; + +/** + * Extends ByteArrayOutputStream, but exposes the internal buffer. This way we don't need to call + * toByteArray() which copies the internal buffer + * @author Bela Ban + * @version $Id: ExposedByteArrayOutputStream.java,v 1.1 2012/08/17 14:51:01 marcin Exp $ + */ +public class ExposedByteArrayOutputStream extends ByteArrayOutputStream { + + public ExposedByteArrayOutputStream() { + super(); + } + + public ExposedByteArrayOutputStream(int size) { + super(size); + } + + /** + * Resets count and creates a new buf if the current buf is > max_size. This method is not synchronized + */ + public void reset(int max_size) { + reset(); + if(buf.length > max_size) { + buf=new byte[max_size]; + } + } + + public byte[] getRawBuffer() { + return buf; + } + + public int getCapacity() { + return buf.length; + } + + + public void write(int b) { + int newcount = count + 1; + if (newcount > buf.length) { + byte newbuf[] = new byte[Math.max(buf.length << 1, newcount)]; + System.arraycopy(buf, 0, newbuf, 0, count); + buf = newbuf; + } + buf[count] = (byte)b; + count = newcount; + } + + /** + * Writes len bytes from the specified byte array + * starting at offset off to this byte array output stream. + * @param b the data. + * @param off the start offset in the data. + * @param len the number of bytes to write. + */ + public void write(byte b[], int off, int len) { + if ((off < 0) || (off > b.length) || (len < 0) || + ((off + len) > b.length) || ((off + len) < 0)) { + throw new IndexOutOfBoundsException(); + } else if (len == 0) { + return; + } + int newcount = count + len; + if (newcount > buf.length) { + byte newbuf[] = new byte[Math.max(buf.length << 1, newcount)]; + System.arraycopy(buf, 0, newbuf, 0, count); + buf = newbuf; + } + System.arraycopy(b, off, buf, count, len); + count = newcount; + } + + /** + * Writes the complete contents of this byte array output stream to + * the specified output stream argument, as if by calling the output + * stream's write method using out.write(buf, 0, count). + * @param out the output stream to which to write the data. + * @throws java.io.IOException if an I/O error occurs. + */ + public synchronized void writeTo(OutputStream out) throws IOException { + out.write(buf, 0, count); + } + + /** + * Resets the count field of this byte array output + * stream to zero, so that all currently accumulated output in the + * output stream is discarded. The output stream can be used again, + * reusing the already allocated buffer space. + * @see java.io.ByteArrayInputStream#count + */ + public void reset() { + count=0; + } + + /** + * Creates a newly allocated byte array. Its size is the current + * size of this output stream and the valid contents of the buffer + * have been copied into it. + * @return the current contents of this output stream, as a byte array. + * @see java.io.ByteArrayOutputStream#size() + */ + public synchronized byte toByteArray()[] { + byte newbuf[] = new byte[count]; + System.arraycopy(buf, 0, newbuf, 0, count); + return newbuf; + } + + /** + * Returns the current size of the buffer. + * @return the value of the count field, which is the number + * of valid bytes in this output stream. + * @see java.io.ByteArrayOutputStream#count + */ + public int size() { + return count; + } + + /** + * Converts the buffer's contents into a string decoding bytes using the + * platform's default character set. The length of the new String + * is a function of the character set, and hence may not be equal to the + * size of the buffer. + *

        + *

        This method always replaces malformed-input and unmappable-character + * sequences with the default replacement string for the platform's + * default character set. The {@linkplain java.nio.charset.CharsetDecoder} + * class should be used when more control over the decoding process is + * required. + * @return String decoded from the buffer's contents. + * @since JDK1.1 + */ + public String toString() { + return new String(buf, 0, count); + } + + /** + * Converts the buffer's contents into a string by decoding the bytes using + * the specified {@link java.nio.charset.Charset charsetName}. The length of + * the new String is a function of the charset, and hence may not be + * equal to the length of the byte array. + *

        + *

        This method always replaces malformed-input and unmappable-character + * sequences with this charset's default replacement string. The {@link + * java.nio.charset.CharsetDecoder} class should be used when more control + * over the decoding process is required. + * @param charsetName the name of a supported + * {@linkplain java.nio.charset.Charset charset} + * @return String decoded from the buffer's contents. + * @throws java.io.UnsupportedEncodingException + * If the named charset is not supported + * @since JDK1.1 + */ + public String toString(String charsetName) + throws UnsupportedEncodingException { + return new String(buf, 0, count, charsetName); + } + + /** + * Creates a newly allocated string. Its size is the current size of + * the output stream and the valid contents of the buffer have been + * copied into it. Each character c in the resulting string is + * constructed from the corresponding element b in the byte + * array such that: + *

        +     *     c == (char)(((hibyte & 0xff) << 8) | (b & 0xff))
        +     * 
        + * @param hibyte the high byte of each resulting Unicode character. + * @return the current contents of the output stream, as a string. + * @see java.io.ByteArrayOutputStream#size() + * @see java.io.ByteArrayOutputStream#toString(String) + * @see java.io.ByteArrayOutputStream#toString() + * @deprecated This method does not properly convert bytes into characters. + * As of JDK 1.1, the preferred way to do this is via the + * toString(String enc) method, which takes an encoding-name + * argument, or the toString() method, which uses the + * platform's default character encoding. + */ + @Deprecated + public String toString(int hibyte) { + return new String(buf, hibyte, 0, count); + } + + + + +} Index: 3rdParty_sources/jgroups/org/jgroups/util/ExposedDataOutputStream.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/ExposedDataOutputStream.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/ExposedDataOutputStream.java 17 Aug 2012 14:51:02 -0000 1.1 @@ -0,0 +1,79 @@ +package org.jgroups.util; + +import java.io.DataOutputStream; +import java.io.OutputStream; +import java.io.IOException; + +/** + * @author Bela Ban + * @version $Id: ExposedDataOutputStream.java,v 1.1 2012/08/17 14:51:02 marcin Exp $ + */ +public class ExposedDataOutputStream extends DataOutputStream { + /** + * Creates a new data output stream to write data to the specified + * underlying output stream. The counter written is + * set to zero. + * + * @param out the underlying output stream, to be saved for later + * use. + * @see java.io.FilterOutputStream#out + */ + public ExposedDataOutputStream(OutputStream out) { + super(out); + } + + public void reset() { + written=0; + } + + public OutputStream getOutputStream() { + return out; + } + + + /** + * Writes the specified byte (the low eight bits of the argument + * b) to the underlying output stream. If no exception + * is thrown, the counter written is incremented by + * 1. + *

        + * Implements the write method of OutputStream. + * + * @param b the byte to be written. + * @exception java.io.IOException if an I/O error occurs. + * @see java.io.FilterOutputStream#out + */ + public void write(int b) throws IOException { + out.write(b); + incCount(1); + } + + /** + * Writes len bytes from the specified byte array + * starting at offset off to the underlying output stream. + * If no exception is thrown, the counter written is + * incremented by len. + * + * @param b the data. + * @param off the start offset in the data. + * @param len the number of bytes to write. + * @exception IOException if an I/O error occurs. + * @see java.io.FilterOutputStream#out + */ + public void write(byte b[], int off, int len) + throws IOException + { + out.write(b, off, len); + incCount(len); + } + + + private void incCount(int value) { + int temp = written + value; + if (temp < 0) { + temp = Integer.MAX_VALUE; + } + written = temp; + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/util/FIFOMessageQueue.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/FIFOMessageQueue.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/FIFOMessageQueue.java 17 Aug 2012 14:51:00 -0000 1.1 @@ -0,0 +1,125 @@ +package org.jgroups.util; + +import org.jgroups.Address; + +import java.util.LinkedList; +import java.util.Map; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Blocking queue which can only process 1 message per service concurrently, establishing FIFO order per sender. Example: + * if message A1, A2, A3, B1, B2 (where A and B are service names for services on top of a Multiplexer) arrive at the + * same time, then this class will deliver A1 and B1 concurrently (ie. pass them up to the thread pool for processing). + * Only when A1 is done will A2 be processed, same for B2: it will get processed when B1 is done. Thus, messages + * for different services are processed concurrently; messages from the same service are processed FIFO. + * @author Bela Ban + * @version $Id: FIFOMessageQueue.java,v 1.1 2012/08/17 14:51:00 marcin Exp $ + */ +public class FIFOMessageQueue { + /** Used for consolidated takes */ + final BlockingQueue queue=new LinkedBlockingQueue(); + + /** One queue per sender and destination. This is a two level hashmap, with sender's addresses as keys and hashmaps + * as values. Those hashmaps have destinations (K) as keys and Entries (list of Vs) as values */ + final ConcurrentMap>> queues=new ConcurrentHashMap>>(); + + private final AtomicInteger size=new AtomicInteger(0); + + + public V take() throws InterruptedException { + V retval=queue.take(); + if(retval != null) + size.decrementAndGet(); + return retval; + } + + + public V poll(long timeout) throws InterruptedException { + V retval=queue.poll(timeout, TimeUnit.MILLISECONDS); + if(retval != null) + size.decrementAndGet(); + return retval; + } + + + + public void put(Address sender, K dest, V el) throws InterruptedException { + if(sender == null) { + size.incrementAndGet(); + queue.add(el); + return; + } + + ConcurrentMap> dests=queues.get(sender); + if(dests == null) { + dests=new ConcurrentHashMap>(); + if(queues.putIfAbsent(sender, dests) != null) // already existed (someone else inserted the key/value mapping) + dests=queues.get(sender); + } + + Entry entry=dests.get(dest); + if(entry == null) { + entry=new Entry(); + if(dests.putIfAbsent(dest, entry) != null) + entry=dests.get(dest); + } + + synchronized(entry) { + size.incrementAndGet(); + if(entry.ready) { + entry.ready=false; + queue.add(el); + } + else { + entry.list.add(el); + } + } + } + + + public void done(Address sender, K dest) { + if(sender == null) + return; + Map> dests=queues.get(sender); + if(dests == null) return; + + Entry entry=dests.get(dest); + if(entry != null) { + V el=null; + synchronized(entry) { + if(!entry.list.isEmpty()) { + el=entry.list.removeFirst(); + queue.add(el); + } + else { + entry.ready=true; + } + } + } + } + + public int size() { + return size.get(); + } + + public String toString() { + StringBuilder sb=new StringBuilder(); + sb.append("queue: ").append(queue).append("\nqueues:\n"); + for(ConcurrentMap.Entry>> entry: queues.entrySet()) { + sb.append("sender ").append(entry.getKey()).append(":\n"); + for(Map.Entry> entry2: entry.getValue().entrySet()) { + sb.append(entry2.getKey()).append(": ").append(entry2.getValue().list).append("\n"); + } + } + return sb.toString(); + } + + + + static class Entry { + boolean ready=true; + LinkedList list=new LinkedList(); + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/util/GetNetworkInterfaces.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/GetNetworkInterfaces.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/GetNetworkInterfaces.java 17 Aug 2012 14:51:00 -0000 1.1 @@ -0,0 +1,30 @@ +package org.jgroups.util; + +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.util.Enumeration; + +/** + * Lists all network interfaces on a system + * @author Bela Ban Dec 18 + * @author 2003 + * @version $Id: GetNetworkInterfaces.java,v 1.1 2012/08/17 14:51:00 marcin Exp $ + */ +public class GetNetworkInterfaces { + + public static void main(String[] args) throws SocketException { + Enumeration en=NetworkInterface.getNetworkInterfaces(); + while(en.hasMoreElements()) { + NetworkInterface i=(NetworkInterface)en.nextElement(); + System.out.println(i.getName() + ':'); + System.out.println(" \t" + i.getDisplayName()); + for(Enumeration en2=i.getInetAddresses(); en2.hasMoreElements();) { + InetAddress addr=(InetAddress)en2.nextElement(); + System.out.println(" \t" + addr + " (" + addr.getHostName() + ')'); + } + System.out.println("---------------------"); + } + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/util/Headers.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/Headers.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/Headers.java 17 Aug 2012 14:51:02 -0000 1.1 @@ -0,0 +1,204 @@ +package org.jgroups.util; + +import org.jgroups.Global; +import org.jgroups.Header; + +import java.util.HashMap; +import java.util.Map; + +/** + * Open addressing based implementation of a hashmap (not supporting the Map interface though) for message + * headers. The keys are strings and the values Headers, and they're stored in an array in the format + * key-1 | header-1 | key-2 | header-2. The array is populated from left to right, so any null slots can terminate + * an interation, or signal empty slots. + *
        + * It is assumed that we only have a few headers, 3-4 on average. Note that getting a header for a given key and + * putting a new key/header are operations with O(n) cost, so this implementation is not recommended for + * a large number of elements. + *
        + * This class is not synchronized + * @author Bela Ban + * @version $Id: Headers.java,v 1.1 2012/08/17 14:51:02 marcin Exp $ + */ +public class Headers { + /** Used to store strings and headers, e.g: name-1 | header-1 | name-2 | header-2 | null | null | name-3 | header-3 */ + private Object[] data; + + /** Add space for 3 new elements when resizing */ + private static final int RESIZE_INCR=6; + + public Headers(int initial_capacity) { + data=new Object[initial_capacity << 1]; + } + + public Headers(Headers hdrs) { + data=new Object[hdrs.data.length]; + System.arraycopy(hdrs.data, 0, this.data, 0, hdrs.data.length); + } + + public Object[] getRawData() { + return data; + } + + /** + * Returns the header associated with key + * @param key + * @return + */ + public Header getHeader(String key) { + for(int i=0; i < data.length; i+=2) { + if(data[i] == null) + return null; + if(data[i].equals(key)) + return (Header)data[i+1]; + } + return null; + } + + public Map getHeaders() { + Map retval=new HashMap(data.length / 2); + for(int i=0; i < data.length; i+=2) { + if(data[i] != null) + retval.put((String)data[i], (Header)data[i+1]); + else + break; + } + return retval; + } + + public String printHeaders() { + StringBuilder sb=new StringBuilder(); + boolean first=true; + for(int i=0; i < data.length; i+=2) { + if(data[i] != null) { + if(first) + first=false; + else + sb.append(", "); + sb.append(data[i]).append(": ").append(data[i+1]); + } + else + break; + } + return sb.toString(); + } + + + /** Puts a header given a key into the hashmap. Overwrites potential existing entry. */ + public void putHeader(String key, Header hdr) { + _putHeader(key, hdr, 0, true); + } + + + + + /** + * Puts a header given a key into the map, only if the key doesn't exist yet + * @param key + * @param hdr + * @return the previous value associated with the specified key, or + * null if there was no mapping for the key. + * (A null return can also indicate that the map + * previously associated null with the key, + * if the implementation supports null values.) + */ + public Header putHeaderIfAbsent(String key, Header hdr) { + return _putHeader(key, hdr, 0, false); + } + + /** + * + * @param key + * @return the header assoaicted with key + * @deprecated Use getHeader() instead. The issue with removing a header is described in + * http://jira.jboss.com/jira/browse/JGRP-393 + */ + public Header removeHeader(String key) { + return getHeader(key); + } + + public Headers copy() { + return new Headers(this); + } + + public int marshalledSize() { + int retval=0; + for(int i=0; i < data.length; i+=2) { + if(data[i] != null) { + retval+=((String)data[i]).length() +2; + retval+=(Global.SHORT_SIZE *2); // 2 for magic number, 2 for size (short) + retval+=((Header)data[i+1]).size(); + } + else + break; + } + return retval; + } + + public int size() { + int retval=0; + for(int i=0; i < data.length; i+=2) { + if(data[i] != null) + retval++; + else + break; + } + return retval; + } + + public int capacity() { + return data.length / 2; + } + + public String printObjectHeaders() { + StringBuilder sb=new StringBuilder(); + for(int i=0; i < data.length; i+=2) { + if(data[i] != null) + sb.append(data[i]).append(": ").append(data[i+1]).append('\n'); + else + break; + } + return sb.toString(); + } + + public String toString() { + return printHeaders(); + } + + + /** + * Increases the capacity of the array and copies the contents of the old into the new array + */ + private void resize() { + int new_size=data.length + RESIZE_INCR; + Object[] new_data=new Object[new_size]; + System.arraycopy(data, 0, new_data, 0, data.length); + data=new_data; + } + + + private Header _putHeader(String key, Header hdr, int start_index, boolean replace_if_present) { + int i=start_index; + while(i < data.length) { + if(data[i] == null) { + data[i]=key; + data[i+1]=hdr; + return null; + } + if(data[i].equals(key)) { + Header retval=(Header)data[i+1]; + if(replace_if_present) { + data[i+1]=hdr; + } + return retval; + } + i+=2; + if(i >= data.length) { + resize(); + } + } + throw new IllegalStateException("unable to add element " + key + ", index=" + i); // we should never come here + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/util/LazyThreadFactory.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/LazyThreadFactory.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/LazyThreadFactory.java 17 Aug 2012 14:51:01 -0000 1.1 @@ -0,0 +1,89 @@ +package org.jgroups.util; + +import java.lang.ref.WeakReference; +import java.util.Collection; +import java.util.Iterator; +import java.util.concurrent.ConcurrentLinkedQueue; + +/** + * Lazily names threads: whenever the address or cluster name is changed, all threads are renamed + * @author Bela Ban + * @version $Id: LazyThreadFactory.java,v 1.1 2012/08/17 14:51:01 marcin Exp $ + */ +public class LazyThreadFactory extends DefaultThreadFactory { + private Collection> threads=new ConcurrentLinkedQueue>(); + private static final StringBuilder ADDR=new StringBuilder(""); + private static final StringBuilder CL_NAME=new StringBuilder(""); + + public LazyThreadFactory(ThreadGroup group, String baseName, boolean createDaemons) { + super(group, baseName, createDaemons); + } + + public LazyThreadFactory(ThreadGroup group, String baseName, boolean createDaemons, boolean use_numbering) { + super(group, baseName, createDaemons, use_numbering); + } + + public Thread newThread(ThreadGroup group, Runnable r, String name) { + Thread retval=null; + String addr=address; + if(addr == null) + addr=""; + String cluster_name=clusterName; + if(cluster_name == null) + cluster_name=""; + + retval=super.newThread(group, r, name, addr, cluster_name); + threads.add(new WeakReference(retval)); + return retval; + } + + + public void setAddress(String address) { + boolean changed=false; + if(!Util.match(this.address, address)) + changed=true; + super.setAddress(address); + if(changed) + renameThreads(); + } + + public void setClusterName(String cluster_name) { + boolean changed=false; + if(!Util.match(this.clusterName, cluster_name)) + changed=true; + super.setClusterName(cluster_name); + if(changed) + renameThreads(); + } + + protected void renameThreads() { + for(Iterator> it=threads.iterator(); it.hasNext();) { + WeakReference ref=it.next(); + Thread thread=ref.get(); + if(thread == null) { + it.remove(); + continue; + } + String name=thread.getName(); + name=changeName(name); + thread.setName(name); + } + } + + /** Replaces "" with the address and with cluster name */ + private String changeName(String name) { + String retval=name; + StringBuilder tmp; + + if(address != null) { + tmp=new StringBuilder(address); + retval=retval.replace(ADDR, tmp); + } + if(clusterName != null) { + tmp=new StringBuilder(clusterName); + retval=retval.replace(CL_NAME, tmp); + } + + return retval; + } +} Index: 3rdParty_sources/jgroups/org/jgroups/util/MagicObjectInputStream.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/MagicObjectInputStream.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/MagicObjectInputStream.java 17 Aug 2012 14:51:02 -0000 1.1 @@ -0,0 +1,51 @@ +package org.jgroups.util; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.ChannelException; +import org.jgroups.conf.ClassConfigurator; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectStreamClass; + +/** + * Uses magic numbers for class descriptors + * @author Bela Ban + * @version $Id: MagicObjectInputStream.java,v 1.1 2012/08/17 14:51:02 marcin Exp $ + */ +public class MagicObjectInputStream extends ContextObjectInputStream { + static volatile ClassConfigurator conf=null; + static final Log log=LogFactory.getLog(MagicObjectInputStream.class); + + + public MagicObjectInputStream(InputStream is) throws IOException { + super(is); + if(conf == null) { + try { + conf=ClassConfigurator.getInstance(false); + } + catch(ChannelException e) { + log.error("ClassConfigurator could not be instantiated", e); + } + } + } + + + protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException { + ObjectStreamClass retval; + short magic_num=super.readShort(); + + if(conf == null || magic_num == -1) { + return super.readClassDescriptor(); + } + + retval=conf.getObjectStreamClassFromMagicNumber(magic_num); + if(retval == null) + throw new ClassNotFoundException("failed fetching class descriptor for magic number " + magic_num); + //if(log.isTraceEnabled()) + //log.trace("reading descriptor (from " + magic_num + "): " + retval.getName()); + return retval; + } +} + Index: 3rdParty_sources/jgroups/org/jgroups/util/MagicObjectOutputStream.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/MagicObjectOutputStream.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/MagicObjectOutputStream.java 17 Aug 2012 14:51:02 -0000 1.1 @@ -0,0 +1,56 @@ +package org.jgroups.util; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.ChannelException; +import org.jgroups.conf.ClassConfigurator; + +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamClass; +import java.io.OutputStream; + +/** + * Uses magic numbers for class descriptors + * @author Bela Ban + * @version $Id: MagicObjectOutputStream.java,v 1.1 2012/08/17 14:51:02 marcin Exp $ + */ +public class MagicObjectOutputStream extends ObjectOutputStream { + static volatile ClassConfigurator conf=null; + static final Log log=LogFactory.getLog(MagicObjectOutputStream.class); + + + public MagicObjectOutputStream(OutputStream out) throws IOException { + super(out); + if(conf == null) { + try { + conf=ClassConfigurator.getInstance(false); + } + catch(ChannelException e) { + log.error("ClassConfigurator could not be instantiated", e); + } + } + } + + protected void writeClassDescriptor(ObjectStreamClass desc) throws IOException { + short magic_num; + if(conf == null) { + super.writeShort(-1); + super.writeClassDescriptor(desc); + return; + } + magic_num=conf.getMagicNumberFromObjectStreamClass(desc); + super.writeShort(magic_num); + if(magic_num == -1) { + if(log.isTraceEnabled()) // todo: remove + log.trace("could not find magic number for '" + desc.getName() + "': writing full class descriptor"); + super.writeClassDescriptor(desc); + } + else { + //if(log.isTraceEnabled()) + // log.trace("writing descriptor (num=" + magic_num + "): " + desc.getName()); + } + } + +} + Index: 3rdParty_sources/jgroups/org/jgroups/util/Marshaller.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/Marshaller.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/Marshaller.java 17 Aug 2012 14:51:01 -0000 1.1 @@ -0,0 +1,142 @@ +// $Id: Marshaller.java,v 1.1 2012/08/17 14:51:01 marcin Exp $ + +package org.jgroups.util; + + +import org.jgroups.conf.ClassConfigurator; +import org.jgroups.ChannelException; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; + + + + +/** + * Title: JGroups Communications + * Description: Contact me at mail@filip.net + * Copyright: Copyright (c) 2002 + * Company: www.filip.net + * @author Filip Hanik + * @author Bela Ban + * @version 1.0 + * + * This class marshalls classes, in other words it serializes and deserializes classes + * to and from object streams. + * It performs a magic number matching to decrease the number of bytes that are being sent + * over the wire. + * If no magic number is available for the class, the classname is sent over instead + */ +public class Marshaller { + /** + * The class configurator maps classes to magic numbers + */ + private static final ClassConfigurator mConfigurator; + + static { + try { + mConfigurator=ClassConfigurator.getInstance(true); + } + catch(ChannelException e) { + throw new ExceptionInInitializerError(e.toString()); + } + } + + + public Marshaller() { + } + + /** + * reads the magic number, instantiates the class (from the + * configurator) and invokes the readExternal method on the object. + * If no magic number is present, the method will read the + * string and then get the class from the configurator. + * @param in an ObjectInput stream - the stream should be composed as follows:
        + * [boolean -> int|string -> object data] + *
        + * If the boolean is true, then the next value is an int, the magic number.
        + * If the boolean is false, then the next value is a string (the class name)
        + * The object data is what the object instance uses to populate its fields
        + */ + public static Externalizable read(ObjectInput in) throws IOException { + try { + boolean is_null=in.readBoolean(); + if(is_null) + return null; + + //see if we want to use a magic number + boolean usemagic=in.readBoolean(); + //the class that we will use to instantiate the object with + Class extclass=null; + if(usemagic) { + //read the magic number + short magic=in.readShort(); + //from the magic number, get the class + extclass=mConfigurator.get(magic); + } + else { + //we don't have a magic number, read the class name + String magic=in.readUTF(); + //get the class, ie let the configurator load it + extclass=mConfigurator.get(magic); + }//end if + //instantiate the object + Externalizable newinstance=(Externalizable)extclass.newInstance(); + //populate the object with its data + newinstance.readExternal(in); + //return the instance + return newinstance; + } + catch(Throwable x) { + if(x instanceof IOException) + throw (IOException)x; + else + throw new IOException(x.toString()); + } + } + + /** + * Writes an object to the ObjectOutput stream. + * If possible, we will send over a magic number instead of the class name + * so that we transfer less amount of data. + * @param inst - an object instance to be serialized, can not be null + * @param out - the ObjectOutput stream we will write the serialized data to + */ + public static void write(Externalizable inst, ObjectOutput out) throws IOException { + boolean is_null=(inst == null); + try { + // if inst is a null value we write this first + out.writeBoolean(is_null); + if(is_null) + return; + + //find out if we have a magic number for this class + short magic=mConfigurator.getMagicNumber(inst.getClass()); + //-1 means no magic number otherwise we have one + if(magic != -1) { + //true means we use a magic number + out.writeBoolean(true); + //write the magic number + out.writeShort(magic); + } + else { + //we don't have a magic number + out.writeBoolean(false); + //write the classname instead + out.writeUTF(inst.getClass().getName()); + }//end if + //write the object data + inst.writeExternal(out); + } + catch(Exception x) { + if(x instanceof IOException) + throw (IOException)x; + else + throw new java.io.IOException(x.toString()); + } + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/util/MutableDigest.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/MutableDigest.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/MutableDigest.java 17 Aug 2012 14:51:01 -0000 1.1 @@ -0,0 +1,200 @@ +package org.jgroups.util; + +import org.jgroups.Address; + +import java.util.Iterator; +import java.util.Map; + +/** + * A mutable version of Digest (which is immutable + * @author Bela Ban + * @version $Id: MutableDigest.java,v 1.1 2012/08/17 14:51:01 marcin Exp $ + */ +public class MutableDigest extends Digest { + private boolean sealed=false; + + /** Used for externalization */ + public MutableDigest() { + super(); + } + + public MutableDigest(int size) { + super(size); + } + + + public MutableDigest(Map map) { + super(map); + } + + + public MutableDigest(Digest digest) { + super(digest.getSenders()); + } + + + public Map getSenders() { + return senders; + } + + public void add(Address sender, long low_seqno, long highest_delivered_seqno) { + checkSealed(); + add(sender, low_seqno, highest_delivered_seqno, -1); + } + + + public void add(Address sender, long low_seqno, long highest_delivered_seqno, long highest_received_seqno) { + checkSealed(); + add(sender, new Digest.Entry(low_seqno, highest_delivered_seqno, highest_received_seqno)); + } + + private void add(Address sender, Entry entry) { + if(sender == null || entry == null) { + if(log.isErrorEnabled()) + log.error("sender (" + sender + ") or entry (" + entry + ")is null, will not add entry"); + return; + } + checkSealed(); + Object retval=senders.put(sender, entry); + if(retval != null && log.isWarnEnabled()) + log.warn("entry for " + sender + " was overwritten with " + entry); + } + + + public void add(Digest digest) { + if(digest != null) { + checkSealed(); + Map.Entry entry; + Address key; + Entry val; + for(Iterator> it=digest.senders.entrySet().iterator(); it.hasNext();) { + entry=it.next(); + key=entry.getKey(); + val=entry.getValue(); + add(key, val.getLow(), val.getHighestDeliveredSeqno(), val.getHighestReceivedSeqno()); + } + } + } + + public void replace(Digest d) { + if(d != null) { + clear(); + add(d); + } + } + + public boolean set(Address sender, long low_seqno, long highest_delivered_seqno, long highest_received_seqno) { + checkSealed(); + Entry entry=senders.put(sender, new Entry(low_seqno, highest_delivered_seqno, highest_received_seqno)); + return entry == null; + } + + /** + * Adds a digest to this digest. This digest must have enough space to add the other digest; otherwise an error + * message will be written. For each sender in the other digest, the merge() method will be called. + */ + public void merge(Digest digest) { + if(digest == null) { + if(log.isErrorEnabled()) log.error("digest to be merged with is null"); + return; + } + checkSealed(); + Map.Entry entry; + Address sender; + Entry val; + for(Iterator> it=digest.senders.entrySet().iterator(); it.hasNext();) { + entry=it.next(); + sender=entry.getKey(); + val=entry.getValue(); + if(val != null) { + merge(sender, val.getLow(), val.getHighestDeliveredSeqno(), val.getHighestReceivedSeqno()); + } + } + } + + + /** + * Similar to add(), but if the sender already exists, its seqnos will be modified (no new entry) as follows: + *

          + *
        1. this.low_seqno=min(this.low_seqno, low_seqno) + *
        2. this.highest_delivered_seqno=max(this.highest_delivered_seqno, highest_delivered_seqno) + *
        3. this.highest_received_seqno=max(this.highest_received_seqno, highest_received_seqno) + *
        + * If the sender doesn not exist, a new entry will be added (provided there is enough space) + */ + public void merge(Address sender, long low_seqno, long highest_delivered_seqno, long highest_received_seqno) { + if(sender == null) { + if(log.isErrorEnabled()) log.error("sender == null"); + return; + } + checkSealed(); + Entry entry=senders.get(sender); + if(entry == null) { + add(sender, low_seqno, highest_delivered_seqno, highest_received_seqno); + } + else { + Entry new_entry=new Entry(Math.min(entry.getLow(), low_seqno), + Math.max(entry.getHighestDeliveredSeqno(), highest_delivered_seqno), + Math.max(entry.getHighestReceivedSeqno(), highest_received_seqno)); + senders.put(sender, new_entry); + } + } + + + + /** + * Increments the sender's high_seqno by 1. + */ + public void incrementHighestDeliveredSeqno(Address sender) { + Entry entry=senders.get(sender); + if(entry == null) + return; + checkSealed(); + Entry new_entry=new Entry(entry.getLow(), entry.getHighestDeliveredSeqno() +1, entry.getHighestReceivedSeqno()); + senders.put(sender, new_entry); + } + + + /** + * Resets the seqnos for the sender at 'index' to 0. This happens when a member has left the group, + * but it is still in the digest. Resetting its seqnos ensures that no-one will request a message + * retransmission from the dead member. + */ + public void resetAt(Address sender) { + Entry entry=senders.get(sender); + if(entry != null) + checkSealed(); + senders.put(sender, new Entry()); + } + + + public void clear() { + checkSealed(); + senders.clear(); + } + + + + public void setHighestDeliveredAndSeenSeqnos(Address sender, long low_seqno, long highest_delivered_seqno, long highest_received_seqno) { + Entry entry=senders.get(sender); + if(entry != null) { + checkSealed(); + Entry new_entry=new Entry(low_seqno, highest_delivered_seqno, highest_received_seqno); + senders.put(sender, new_entry); + } + } + + /** Seals the instance against modifications */ + public boolean seal() { + boolean retval=sealed; + sealed=true; + return retval; + } + + + private final void checkSealed() { + if(sealed) + throw new IllegalAccessError("instance has been sealed and cannot be modified"); + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/util/OutgoingBufferPool.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/OutgoingBufferPool.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/OutgoingBufferPool.java 17 Aug 2012 14:51:01 -0000 1.1 @@ -0,0 +1,56 @@ +package org.jgroups.util; + +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; + +/** + * Maintains a pool of ExposedDataOutputStreams. The main reason is that a ByteArrayOutputStream starts with 1024 + * bytes, and almost always increases to 65K (max size of a UDP datagram). We save a few copies when the BAOS increases + * its size by pooling those. + * @author Bela Ban + * @version $Id: OutgoingBufferPool.java,v 1.1 2012/08/17 14:51:01 marcin Exp $ + */ +public class OutgoingBufferPool { + private BlockingQueue buffers; + + + public OutgoingBufferPool(int capacity) { + buffers=new ArrayBlockingQueue(capacity); + for(int i=0; i < capacity; i++) { + ExposedByteArrayOutputStream out_stream=new ExposedByteArrayOutputStream(1024); + ExposedDataOutputStream dos=new ExposedDataOutputStream(out_stream); + try { + buffers.put(dos); + } + catch(InterruptedException e) { + Thread.currentThread().interrupt(); + return; + } + } + } + + + public ExposedDataOutputStream take() throws InterruptedException { + return buffers.take(); + } + + public void put(ExposedDataOutputStream buf) throws InterruptedException { + ((ExposedByteArrayOutputStream)buf.getOutputStream()).reset(); + buf.reset(); + buffers.put(buf); + } + + + public String dumpStats() { + ExposedByteArrayOutputStream stream; + StringBuilder sb=new StringBuilder(); + sb.append(buffers.size()).append(" elements, capacities:\n"); + for(ExposedDataOutputStream buf: buffers) { + stream=(ExposedByteArrayOutputStream)buf.getOutputStream(); + sb.append("size=").append(stream.size()).append(", capacity=").append(stream.getCapacity()).append(")\n"); + } + return sb.toString(); + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/util/PortsManager.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/PortsManager.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/PortsManager.java 17 Aug 2012 14:51:01 -0000 1.1 @@ -0,0 +1,176 @@ +package org.jgroups.util; + +import java.io.*; +import java.util.*; + +/** + * Maintains a list of ports used on this host, associated with time stamps. The ports are persistet into the + * temp file system. + * @author Bela Ban + * @version $Id: PortsManager.java,v 1.1 2012/08/17 14:51:01 marcin Exp $ + */ +public class PortsManager { + private String filename="jgroups-ports.txt"; + private String temp_dir=System.getProperty("java.io.tmpdir", "/tmp"); + private final static String file_separator=System.getProperty("file.separator", "/"); + private String absolute_name=temp_dir + file_separator + filename; + + /** Time after which a port can be removed and re-allocated */ + private long expiry_time=60 * 1000L; + + public PortsManager() { + } + + public PortsManager(long expiry_time) { + this.expiry_time=expiry_time; + } + + public PortsManager(String ports_file) { + if(ports_file != null) + this.absolute_name=ports_file; + } + + public PortsManager(long expiry_time, String ports_file) { + this.expiry_time=expiry_time; + if(ports_file != null) + absolute_name=ports_file; + } + + public PortsManager(long expiry_time, String filename, String temp_dir) { + this.expiry_time=expiry_time; + this.filename=filename; + this.temp_dir=temp_dir; + absolute_name=temp_dir + file_separator + filename; + } + + + public long getExpiryTime() { + return expiry_time; + } + + public void setExpiryTime(long expiry_time) { + this.expiry_time=expiry_time; + } + + /** Loads the file, weeds out expired ports, returns the next available port and saves the new port in the file */ + public int getNextAvailablePort(int start_port) { + Map map; + int retval=-1; + + try { + map=load(); + long current_time=System.currentTimeMillis(), timestamp; + if(map.isEmpty()) { + map.put(start_port, current_time); + store(map); + return start_port; + } + + // weed out expired ports + Map.Entry entry; + for(Iterator> it=map.entrySet().iterator(); it.hasNext();) { + entry=it.next(); + timestamp=entry.getValue(); + if(current_time - timestamp >= expiry_time) { + it.remove(); + } + } + + // find next available port + Set ports=map.keySet(); + while(ports.contains(start_port)) { + start_port++; + } + map.put(start_port, System.currentTimeMillis()); + store(map); + return start_port; + } + catch(IOException e) { + return retval; + } + } + + /** Loads the file, removes the port (if existent) and closes the file again */ + public void removePort(int port) { + Map map; + + try { + map=load(); + if(map.isEmpty()) { + return; + } + map.remove(port); + store(map); + } + catch(IOException e) { + } + } + + /** + * Updates the timestamp for the given port + * @param port + */ + public void updatePort(int port) { + Map map; + + try { + map=load(); + if(map.isEmpty()) { + return; + } + map.put(port, System.currentTimeMillis()); + store(map); + } + catch(IOException e) { + } + } + + + /** Deletes the underlying file. Used for unit testing, not recommended for regular use ! */ + public void deleteFile() { + File file=new File(absolute_name); + file.delete(); + } + + + private Map load() throws IOException { + InputStream in=null; + Map retval=new HashMap(); + try { + in=new FileInputStream(absolute_name); + Properties props=new Properties(); + props.load(in); + for(Iterator> it=props.entrySet().iterator(); it.hasNext();) { + Map.Entry entry=it.next(); + String keystr=(String)entry.getKey(), valstr=(String)entry.getValue(); + int key=Integer.parseInt(keystr); + long val=Long.parseLong(valstr); + retval.put(key, val); + } + return retval; + } + catch(Throwable t) { + return retval; + } + finally { + Util.close(in); + } + } + + private void store(Map map) throws IOException { + OutputStream out=null; + try { + out=new FileOutputStream(absolute_name); + Properties props=new Properties(); + for(Map.Entry entry: map.entrySet()) { + String key=entry.getKey().toString(); + String val=entry.getValue().toString(); + props.put(key, val); + } + props.store(out, "Persistent JGroups ports"); + } + finally { + Util.close(out); + } + } +} Index: 3rdParty_sources/jgroups/org/jgroups/util/Promise.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/Promise.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/Promise.java 17 Aug 2012 14:51:02 -0000 1.1 @@ -0,0 +1,173 @@ +package org.jgroups.util; + +import org.jgroups.TimeoutException; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + + +/** + * Allows a thread to submit an asynchronous request and to wait for the result. The caller may choose to check + * for the result at a later time, or immediately and it may block or not. Both the caller and responder have to + * know the promise. + * @author Bela Ban + * @version $Id: Promise.java,v 1.1 2012/08/17 14:51:02 marcin Exp $ + */ +public class Promise { + private final Lock lock=new ReentrantLock(); + private final Condition cond=lock.newCondition(); + private T result=null; + private boolean hasResult=false; + + public Lock getLock() { + return lock; + } + + public Condition getCond() { + return cond; + } + + /** + * Blocks until a result is available, or timeout milliseconds have elapsed + * @param timeout + * @return An object + * @throws TimeoutException If a timeout occurred (implies that timeout > 0) + */ + public T getResultWithTimeout(long timeout) throws TimeoutException { + lock.lock(); + try { + return _getResultWithTimeout(timeout); + } + finally { + cond.signalAll(); + lock.unlock(); + } + } + + + /** + * Blocks until a result is available, or timeout milliseconds have elapsed. Needs to be called with lock held + * @param timeout + * @return An object + * @throws TimeoutException If a timeout occurred (implies that timeout > 0) + */ + private T _getResultWithTimeout(long timeout) throws TimeoutException { + T ret=null; + long time_to_wait=timeout, start; + boolean timeout_occurred=false; + + start=System.currentTimeMillis(); + while(hasResult == false) { + if(timeout <= 0) { + doWait(); + } + else { + if(time_to_wait <= 0) { + timeout_occurred=true; + break; // terminate the while loop + } + else { + doWait(time_to_wait); + time_to_wait=timeout - (System.currentTimeMillis() - start); + } + } + } + + ret=result; + result=null; + hasResult=false; + if(timeout_occurred) + throw new TimeoutException(); + else + return ret; + } + + public T getResult() { + try { + return getResultWithTimeout(0); + } + catch(TimeoutException e) { + return null; + } + } + + /** + * Returns the result, but never throws a TimeoutException; returns null instead. + * @param timeout + * @return Object + */ + public T getResult(long timeout) { + try { + return getResultWithTimeout(timeout); + } + catch(TimeoutException e) { + return null; + } + } + + + private void doWait() { + try {cond.await();} catch(InterruptedException e) {} + } + + private void doWait(long timeout) { + try {cond.await(timeout, TimeUnit.MILLISECONDS);} catch(InterruptedException e) {} + } + + + + + /** + * Checks whether result is available. Does not block. + */ + public boolean hasResult() { + lock.lock(); + try { + return hasResult; + } + finally { + lock.unlock(); + } + } + + /** + * Sets the result and notifies any threads + * waiting for it + */ + public void setResult(T obj) { + lock.lock(); + try { + result=obj; + hasResult=true; + cond.signalAll(); + } + finally { + lock.unlock(); + } + } + + + /** + * Causes all waiting threads to return + */ + public void reset() { + lock.lock(); + try { + result=null; + hasResult=false; + cond.signalAll(); + } + finally { + lock.unlock(); + } + } + + + public String toString() { + return "hasResult=" + Boolean.valueOf(hasResult) + ",result=" + result; + } + + +} \ No newline at end of file Index: 3rdParty_sources/jgroups/org/jgroups/util/Proxy.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/Proxy.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/Proxy.java 17 Aug 2012 14:51:01 -0000 1.1 @@ -0,0 +1,853 @@ +// $Id: Proxy.java,v 1.1 2012/08/17 14:51:01 marcin Exp $ + +package org.jgroups.util; + + +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSocketFactory; +import java.io.*; +import java.net.*; +import java.nio.ByteBuffer; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.util.*; +import java.util.concurrent.Executor; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + + +/** + * Redirects incoming TCP connections to other hosts/ports. All redirections are defined in a file as for example + *
        + * 127.0.0.1:8888=www.ibm.com:80
        + * localhost:80=pop.mail.yahoo.com:110
        + * 
        + * The first line forwards all requests to port 8888 on to www.ibm.com at port 80 (it also forwards the HTTP + * response back to the sender. The second line essentially provides a POP-3 service on port 8110, using + * Yahoo's POP service. This is neat when you're behind a firewall and one of the few services in the outside + * world that are not blocked is port 80 (HHTP).
        + * Note that JDK 1.4 is required for this class. Also, concurrent.jar has to be on the classpath. Note that + * you also need to include jsse.jar/jce.jar (same location as rt.jar) if you want SSL sockets.
        + * To create SSLServerSockets you'll need to do the following: + * Generate a certificate as follows: + *
        + * keytool -genkey -keystore /home/bela/.keystore -keyalg rsa -alias bela -storepass  -keypass 
        + * 
        + * + * Start the Proxy as follows: + *
        + * java -Djavax.net.ssl.keyStore=/home/bela/.keystore -Djavax.net.ssl.keyStorePassword=
        + *      -Djavax.net.ssl.trustStore=/home/bela/.keystore -Djavax.net.ssl.trustStorePassword=
        + *      org.jgroups.util.Proxy -file /home/bela/map.properties
        + * 
        + * Start client as follows: + *
        + * java -Djavax.net.ssl.trustStore=/home/bela/.keystore -Djavax.net.ssl.trustStorePassword= sslclient
        + * 
        + *
        + * To import a certificate into the keystore, use the following steps: + *
        + * openssl x509 -in server.crt -out server.crt.der -outform DER
        + * keytool -import -trustcacerts -alias  -file server.crt.der
        + * 
        + * This will store the server's certificate in the ${user.home}/.keystore key store. + *
        + * Note that an SSL client or server can be debugged by starting it as follows: + *
        -Djava.protocol.handler.pkgs=com.sun.net.ssl.internal.www.protocol -Djavax.net.debug=ssl
        + *
        + * If you run a web browser, simply enter https://: as URL to connect to an SSLServerSocket + *
        Note that we cannot use JDK 1.4's selectors for SSL sockets, as + * getChannel() on an SSL socket doesn't seem to work. + * @todo Check whether SSLSocket.getChannel() or SSLServerSocket.getChannel() works. + * @author Bela Ban + */ +public class Proxy { + InetAddress local=null, remote=null; + int local_port=0, remote_port=0; + static boolean verbose=false; + static boolean debug=false; + String mapping_file=null; // contains a list of src and dest host:port pairs + final HashMap mappings=new HashMap(); // keys=MyInetSocketAddr (src), values=MyInetSocketAddr (dest) + Executor executor; // maintains a thread pool + static final int MIN_THREAD_POOL_SIZE=2; + static final int MAX_THREAD_POOL_SIZE=64; // for processing requests + static final int BUFSIZE=1024; // size of data transfer buffer + + + + public Proxy(InetAddress local, int local_port, InetAddress remote, int remote_port, boolean verbose, boolean debug) { + this.local=local; + this.local_port=local_port; + this.remote=remote; + this.remote_port=remote_port; + Proxy.verbose=verbose; + Proxy.debug=debug; + } + + public Proxy(InetAddress local, int local_port, InetAddress remote, int remote_port, + boolean verbose, boolean debug, String mapping_file) { + this(local, local_port, remote, remote_port, verbose, debug); + this.mapping_file=mapping_file; + } + + public void start() throws Exception { + Map.Entry entry; + Selector selector; + ServerSocketChannel sock_channel; + MyInetSocketAddress key, value; + + if (remote !=null && local !=null) + mappings.put(new InetSocketAddress(local, local_port), new InetSocketAddress(remote, remote_port)); + + if (mapping_file !=null) { + try { + populateMappings(mapping_file); + } + catch (Exception ex) { + log("Failed reading " + mapping_file); + throw ex; + } + } + + log("\nProxy started at " + new java.util.Date()); + + if (verbose) { + log("\nMappings:\n---------"); + for (Iterator it=mappings.entrySet().iterator(); it.hasNext();) { + entry=(Map.Entry) it.next(); + log(toString((InetSocketAddress) entry.getKey()) + " <--> " + + toString((InetSocketAddress) entry.getValue())); + } + log("\n"); + } + + // 1. Create a Selector + selector=Selector.open(); + + // Create a thread pool (Executor) + executor=new ThreadPoolExecutor(MIN_THREAD_POOL_SIZE, MAX_THREAD_POOL_SIZE, 30000, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue(1000)); + + for (Iterator it=mappings.keySet().iterator(); it.hasNext();) { + key=(MyInetSocketAddress) it.next(); + value=(MyInetSocketAddress) mappings.get(key); + + // if either source or destination are SSL, we cannot use JDK 1.4 + // NIO selectors, but have to fall back on separate threads per connection + + if (key.ssl() || value.ssl()) { + // if(2 == 2) { + SocketAcceptor acceptor=new SocketAcceptor(key, value); + executor.execute(acceptor); + continue; + } + + // 2. Create a ServerSocketChannel + sock_channel=ServerSocketChannel.open(); + sock_channel.configureBlocking(false); + sock_channel.socket().bind(key); + + // 3. Register the selector with all server sockets. 'Key' is attachment, so we get it again on + // select(). That way we can associate it with the mappings hashmap to find the corresponding + // value + sock_channel.register(selector, SelectionKey.OP_ACCEPT, key); + } + + // 4. Start main loop. won't return until CTRL-C'ed + loop(selector); + } + + + + /** We handle only non-SSL connections */ + void loop(Selector selector) { + Set ready_keys; + SelectionKey key; + ServerSocketChannel srv_sock; + SocketChannel in_sock, out_sock; + InetSocketAddress src, dest; + + while (true) { + if (verbose) + log("[Proxy] ready to accept connection"); + + // 4. Call Selector.select() + try { + selector.select(); + + // get set of ready objects + ready_keys=selector.selectedKeys(); + for (Iterator it=ready_keys.iterator(); it.hasNext();) { + key=(SelectionKey) it.next(); + it.remove(); + + if (key.isAcceptable()) { + srv_sock=(ServerSocketChannel) key.channel(); + // get server socket and attachment + src=(InetSocketAddress) key.attachment(); + in_sock=srv_sock.accept(); // accept request + if (verbose) + log("Proxy.loop()", "accepted connection from " + toString(in_sock)); + dest=(InetSocketAddress) mappings.get(src); + // find corresponding dest + if (dest == null) { + in_sock.close(); + log("Proxy.loop()", "did not find a destination host for " + src); + continue; + } + else { + if (verbose) + log("Proxy.loop()", "relaying traffic from " + toString(src) + " to " + toString(dest)); + } + + // establish connection to destination host + try { + out_sock=SocketChannel.open(dest); + // uses thread pool (Executor) to handle request, closes socks at end + handleConnection(in_sock, out_sock); + } + catch (Exception ex) { + in_sock.close(); + throw ex; + } + } + } + } + catch (Exception ex) { + log("Proxy.loop()", "exception: " + ex); + } + } + } + + // void handleConnection(Socket in_sock, Socket out_sock) { + // try { + // Relayer r=new Relayer(in_sock, out_sock); + // executor.execute(r); + // r=new Relayer(out_sock, in_sock); + // executor.execute(r); + // } + // catch (Exception ex) { + // log("Proxy.handleConnection()", "exception: " + ex); + // } + // finally { + // close(in_sock, out_sock); + // } + // } + + void handleConnection(SocketChannel in, SocketChannel out) { + try { + _handleConnection(in, out); + } + catch (Exception ex) { + log("Proxy.handleConnection()", "exception: " + ex); + } + } + + void _handleConnection(final SocketChannel in_channel, final SocketChannel out_channel) throws Exception { + executor.execute(new Runnable() { + public void run() { + Selector sel=null; + SocketChannel tmp; + Set ready_keys; + SelectionKey key; + ByteBuffer transfer_buf=ByteBuffer.allocate(BUFSIZE); + + try { + sel=Selector.open(); + in_channel.configureBlocking(false); + out_channel.configureBlocking(false); + in_channel.register(sel, SelectionKey.OP_READ); + out_channel.register(sel, SelectionKey.OP_READ); + + while (sel.select() > 0) { + ready_keys=sel.selectedKeys(); + for (Iterator it=ready_keys.iterator(); it.hasNext();) { + key=(SelectionKey) it.next(); + it.remove(); // remove current entry (why ?) + tmp=(SocketChannel) key.channel(); + if (tmp == null) { + log( + "Proxy._handleConnection()", + "attachment is null, continuing"); + continue; + } + if (key.isReadable()) { // data is available to be read from tmp + if (tmp == in_channel) { + // read all data from in_channel and forward it to out_channel (request) + if (relay(tmp, out_channel, transfer_buf) == false) + return; + } + if (tmp == out_channel) { + // read all data from out_channel and forward it + // to in_channel (response) + if (relay(tmp, in_channel, transfer_buf) == false) + return; + } + } + } + } + } + catch (Exception ex) { + ex.printStackTrace(); + } + finally { + close(sel, in_channel, out_channel); + } + } + }); + } + + void close(Selector sel, SocketChannel in_channel, SocketChannel out_channel) { + try { + if (sel !=null) + sel.close(); + } + catch (Exception ex) { + } + try { + if (in_channel !=null) + in_channel.close(); + } + catch (Exception ex) { + } + try { + if (out_channel !=null) + out_channel.close(); + } + catch (Exception ex) { + } + } + + + /** + * Read all data from from and write it to to. + * Returns false if channel was closed + */ + boolean relay(SocketChannel from, SocketChannel to, ByteBuffer buf) throws Exception { + int num; + StringBuilder sb; + + buf.clear(); + while (true) { + num=from.read(buf); + if (num < 0) + return false; + else + if (num == 0) + return true; + buf.flip(); + if (verbose) { + log(printRelayedData(toString(from), toString(to), buf.remaining())); + } + if (debug) { + sb=new StringBuilder(); + sb.append(new String(buf.array()).trim()); + sb.append('\n'); + log(sb.toString()); + } + to.write(buf); + buf.flip(); + } + } + + String toString(SocketChannel ch) { + StringBuilder sb=new StringBuilder(); + Socket sock; + + if (ch == null) + return null; + if ((sock=ch.socket()) == null) + return null; + sb.append(sock.getInetAddress().getHostName()).append(':').append(sock.getPort()); + return sb.toString(); + } + + String toString(InetSocketAddress addr) { + StringBuilder sb; + sb=new StringBuilder(); + + if (addr == null) + return null; + sb.append(addr.getAddress().getHostName()).append(':').append(addr.getPort()); + if (addr instanceof MyInetSocketAddress) + sb.append(" [ssl=").append(((MyInetSocketAddress) addr).ssl()).append(']'); + return sb.toString(); + } + + + static String printRelayedData(String from, String to, int num_bytes) { + StringBuilder sb; + sb=new StringBuilder(); + sb.append("\n[PROXY] ").append(from); + sb.append(" to ").append(to); + sb.append(" (").append(num_bytes).append(" bytes)"); + // log("Proxy.relay()", sb.toString()); + return sb.toString(); + } + + + /** + * Populates mappings hashmap. An example of a definition file is: + *
        +     * http://localhost:8888=http://www.yahoo.com:80
        +     * https://localhost:2200=https://cvs.sourceforge.net:22
        +     * http://localhost:8000=https://www.ibm.com:443
        +     * 
        + * Mappings can be http-https, https-http, http-http or https-https + */ + void populateMappings(String filename) throws Exception { + FileInputStream in=new FileInputStream(filename); + BufferedReader reader; + String line; + URI key, value; + int index; + boolean ssl_key, ssl_value; + final String HTTPS="https"; + + reader=new BufferedReader(new InputStreamReader(in)); + while ((line=reader.readLine()) !=null) { + line=line.trim(); + if (line.startsWith("//") || line.startsWith("#") || line.length() == 0) + continue; + index=line.indexOf('='); + if (index == -1) + throw new Exception("Proxy.populateMappings(): detected no '=' character in " + line); + key=new URI(line.substring(0, index)); + ssl_key=key.getScheme().trim().equals(HTTPS); + + value=new URI(line.substring(index + 1)); + ssl_value=value.getScheme().trim().equals(HTTPS); + + check(key); + check(value); + + log("key: " + key + ", value: " + value); + + mappings.put(new MyInetSocketAddress(key.getHost(), key.getPort(), ssl_key), + new MyInetSocketAddress(value.getHost(), value.getPort(), ssl_value)); + } + in.close(); + } + + /** Checks whether a URI is http(s)://: */ + void check(URI u) throws Exception { + if (u.getScheme() == null) + throw new Exception( + "scheme is null in " + u + ", (valid URI is \"http(s)://:\")"); + + if (u.getHost() == null) + throw new Exception( + "host is null in " + u + ", (valid URI is \"http(s)://:\")"); + + if (u.getPort() <=0) + throw new Exception( + "port is <=0 in " + u + ", (valid URI is \"http(s)://:\")"); + + } + + /** Input is "host:port" */ + SocketAddress strToAddr(String input) throws Exception { + StringTokenizer tok=new StringTokenizer(input, ":"); + String host, port; + + host=tok.nextToken(); + port=tok.nextToken(); + return new InetSocketAddress(host, Integer.parseInt(port)); + } + + String printSelectionOps(SelectionKey key) { + StringBuilder sb=new StringBuilder(); + if ((key.readyOps() & SelectionKey.OP_ACCEPT) !=0) + sb.append("OP_ACCEPT "); + if ((key.readyOps() & SelectionKey.OP_CONNECT) !=0) + sb.append("OP_CONNECT "); + if ((key.readyOps() & SelectionKey.OP_READ) !=0) + sb.append("OP_READ "); + if ((key.readyOps() & SelectionKey.OP_WRITE) !=0) + sb.append("OP_WRITE "); + return sb.toString(); + } + + public static void main(String[] args) { + Proxy p; + InetAddress local=null, remote=null; + int local_port=0, remote_port=0; + String tmp, tmp_addr, tmp_port; + boolean verbose=false, debug=false; + int index; + String mapping_file=null; + + try { + for (int i=0; i < args.length; i++) { + tmp=args[i]; + if ("-help".equals(tmp)) { + help(); + return; + } + if ("-verbose".equals(tmp)) { + verbose=true; + continue; + } + if ("-local".equals(tmp)) { + tmp_addr=args[++i]; + index=tmp_addr.indexOf(':'); + if (index > -1) { // it is in the format address:port + tmp_port=tmp_addr.substring(index + 1); + local_port=Integer.parseInt(tmp_port); + tmp_addr=tmp_addr.substring(0, index); + local=InetAddress.getByName(tmp_addr); + } + else + local=InetAddress.getByName(args[++i]); + continue; + } + if ("-local_port".equals(tmp)) { + local_port=Integer.parseInt(args[++i]); + continue; + } + if ("-remote".equals(tmp)) { + tmp_addr=args[++i]; + index=tmp_addr.indexOf(':'); + if (index > -1) { // it is in the format address:port + tmp_port=tmp_addr.substring(index + 1); + remote_port=Integer.parseInt(tmp_port); + tmp_addr=tmp_addr.substring(0, index); + remote=InetAddress.getByName(tmp_addr); + } + else + remote=InetAddress.getByName(args[++i]); + continue; + } + if ("-remote_port".equals(tmp)) { + remote_port=Integer.parseInt(args[++i]); + continue; + } + if ("-file".equals(tmp)) { + mapping_file=args[++i]; + continue; + } + if ("-debug".equals(tmp)) { + debug=true; + continue; + } + help(); + return; + } + + if (local == null) + local=InetAddress.getLocalHost(); + + p=new Proxy(local, local_port, remote, remote_port, verbose, debug, mapping_file); + p.start(); + } + catch (Throwable ex) { + ex.printStackTrace(); + } + } + + static void help() { + System.out.println("Proxy [-help] [-local ] [-local_port ] " + + "[-remote ] [-remote_port ] [-verbose] " + + "[-file ] [-debug]"); + } + + static void log(String method_name, String msg) { + System.out.println('[' + method_name + "]: " + msg); + } + + static void log(String msg) { + System.out.println(msg); + } + + static void close(Socket in, Socket out) { + if (in !=null) { + try { + in.close(); + } + catch (Exception ex) { + } + } + if (out !=null) { + try { + out.close(); + } + catch (Exception ex) { + } + } + } + + static void close(Socket sock) { + if (sock !=null) { + try { + sock.close(); + } + catch (Exception ex) { + } + } + } + + static class Relayer implements Runnable { + final Socket in_sock; + final Socket out_sock; + final InputStream in; + final OutputStream out; + Thread t=null; + final java.util.List listeners=new ArrayList(); + String name=null; + + interface Listener { + void connectionClosed(); + } + + public Relayer(Socket in_sock, Socket out_sock, String name) throws Exception { + this.in_sock=in_sock; + this.out_sock=out_sock; + this.name=name; + in=in_sock.getInputStream(); + out=out_sock.getOutputStream(); + } + + public void addListener(Listener l) { + if(l != null && !listeners.contains(l)) + listeners.add(l); + } + + + public void run() { + byte[] buf=new byte[1024]; + int num; + StringBuilder sb; + + try { + while(t != null) { + if ((num=in.read(buf)) == -1) + break; + + if (verbose) { + + //sb=new StringBuilder(); + + + //sb.append("forwarding ").append(num).append(" bytes from ").append(toString(in_sock)); + //sb.append(" to ").append(toString(out_sock)); + // log("Proxy.Relayer.run()", sb.toString()); + log(printRelayedData(toString(in_sock), toString(out_sock), num)); + } + if (debug) { + sb=new StringBuilder(); + sb.append(new String(buf, 0, num).trim()); + log(sb.toString()); + } + + out.write(buf, 0, num); + //if(debug) + // System.out.println(new String(buf)); + } + + } + catch (Exception ex) { + log("Proxy.Relayer.run(): [" + name + "] exception=" + ex + ", in_sock=" + + in_sock + ", out_sock=" + out_sock); + } + finally { + stop(); + } + } + + public void start() { + if(t == null) { + t=new Thread(this, "Proxy.Relayer"); + t.setDaemon(true); + t.start(); + } + } + + public void stop() { + t=null; + close(in_sock); + close(out_sock); + } + + String toString(Socket s) { + if(s == null) return null; + return s.getInetAddress().getHostName() + ':' + s.getPort(); + } + + + void notifyListeners() { + for(Iterator it=listeners.iterator(); it.hasNext();) { + try { + ((Listener)it.next()).connectionClosed(); + } + catch(Throwable ex) { + ; + } + } + } + } + + static class MyInetSocketAddress extends InetSocketAddress { + boolean is_ssl=false; + + public MyInetSocketAddress(InetAddress addr, int port) { + super(addr, port); + } + + public MyInetSocketAddress(InetAddress addr, int port, boolean is_ssl) { + super(addr, port); + this.is_ssl=is_ssl; + } + + public MyInetSocketAddress(int port) { + super(port); + } + + public MyInetSocketAddress(int port, boolean is_ssl) { + super(port); + this.is_ssl=is_ssl; + } + + public MyInetSocketAddress(String hostname, int port) { + super(hostname, port); + } + + public MyInetSocketAddress(String hostname, int port, boolean is_ssl) { + super(hostname, port); + this.is_ssl=is_ssl; + } + + public boolean ssl() { + return is_ssl; + } + + public String toString() { + return super.toString() + " [ssl: " + ssl() + ']'; + } + } + + /** + * Handles accepts on an SSLServerSocket or ServerSocket. Creates a {@link + * Connection} for each successful accept(). + * + * @author bela Dec 19, 2002 + */ + class SocketAcceptor implements Runnable { + ServerSocket srv_sock=null; + MyInetSocketAddress dest=null; + + + /** + * Create an SSLServerSocket or ServerSocket and continuously call + * accept() on it. + * @param sock_addr + */ + public SocketAcceptor(MyInetSocketAddress sock_addr, MyInetSocketAddress dest) throws Exception { + this.dest=dest; + if(sock_addr.ssl()) { + srv_sock=createSSLServerSocket(sock_addr); + } + else { + srv_sock=createServerSocket(sock_addr); + } + executor.execute(this); + } + + public void run() { + Connection conn; + Socket s, dest_sock; + + while (srv_sock !=null) { + try { + s=srv_sock.accept(); + dest_sock=dest.ssl() ? createSSLSocket(dest) : createSocket(dest); + conn=new Connection(s, dest_sock); + conn.start(); + } + catch (Exception e) { + log("Proxy.SSLServerSocketAcceptor.run(): exception=" + e); + break; + } + } + } + + + Socket createSocket(InetSocketAddress addr) throws Exception { + return new Socket(addr.getAddress(), addr.getPort()); + } + + Socket createSSLSocket(InetSocketAddress addr) throws Exception { + SSLSocketFactory sslsocketfactory = (SSLSocketFactory)SSLSocketFactory.getDefault(); + return sslsocketfactory.createSocket(addr.getAddress(), addr.getPort()); + } + + ServerSocket createServerSocket(InetSocketAddress addr) throws Exception { + return new ServerSocket(addr.getPort(), 10, addr.getAddress()); + } + + ServerSocket createSSLServerSocket(InetSocketAddress addr) throws Exception { + SSLServerSocketFactory sslserversocketfactory = + (SSLServerSocketFactory)SSLServerSocketFactory.getDefault(); + SSLServerSocket sslserversocket; + sslserversocket=(SSLServerSocket)sslserversocketfactory.createServerSocket(addr.getPort(), 10, addr.getAddress()); + return sslserversocket; + } + } + + + + /** + * Handles an incoming SSLSocket or Socket. Looks up the destination in the + * mapping hashmap, key is the incoming socket address. Creates an outgoing + * socket (regular or SSL, depending on settings) and relays data between + * incoming and outgoing sockets. Closes the connection when either incoming + * or outgoing socket is closed, or when stop() is called. + * + * @author bela Dec 19, 2002 + */ + static class Connection implements Relayer.Listener { + Relayer in_to_out=null; + Relayer out_to_in=null; + + /** + * Creates an outgoing (regular or SSL) socket according to the mapping + * table. Sets both input and output stream. Caller needs to call + * start() after the instance has been created. + * @param in The Socket we got as result of accept() + * @throws Exception Thrown if either the input or output streams cannot + * be created. + */ + public Connection(Socket in, Socket out) throws Exception { + in_to_out=new Relayer(in, out, "in-out"); + in_to_out.addListener(this); + out_to_in=new Relayer(out, in, "out-in"); + out_to_in.addListener(this); + } + + /** Starts relaying between incoming and outgoing sockets. + * Returns immediately (thread is started). + * + */ + public void start() { + in_to_out.start(); + out_to_in.start(); + } + + public void stop() { + if (in_to_out !=null) { + in_to_out.stop(); + } + if (out_to_in !=null) { + out_to_in.stop(); + } + } + + public void connectionClosed() { + stop(); + } + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/util/Queue.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/Queue.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/Queue.java 17 Aug 2012 14:51:02 -0000 1.1 @@ -0,0 +1,627 @@ +// $Id: Queue.java,v 1.1 2012/08/17 14:51:02 marcin Exp $ + +package org.jgroups.util; + + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.TimeoutException; + +import java.util.*; + + +/** + * Elements are added at the tail and removed from the head. Class is thread-safe in that + * 1 producer and 1 consumer may add/remove elements concurrently. The class is not + * explicitely designed for multiple producers or consumers. Implemented as a linked + * list, so that removal of an element at the head does not cause a right-shift of the + * remaining elements (as in a Vector-based implementation). + * @author Bela Ban + */ +public class Queue { + + /*head and the tail of the list so that we can easily add and remove objects*/ + private Element head=null, tail=null; + + /*flag to determine the state of the queue*/ + private boolean closed=false; + + /*current size of the queue*/ + private int size=0; + + /* Lock object for synchronization. Is notified when element is added */ + private final Object mutex=new Object(); + + /** Lock object for syncing on removes. It is notified when an object is removed */ + // Object remove_mutex=new Object(); + + /*the number of end markers that have been added*/ + private int num_markers=0; + + /** + * if the queue closes during the runtime + * an endMarker object is added to the end of the queue to indicate that + * the queue will close automatically when the end marker is encountered + * This allows for a "soft" close. + * @see Queue#close + */ + private static final Object endMarker=new Object(); + + protected static final Log log=LogFactory.getLog(Queue.class); + + + /** + * the class Element indicates an object in the queue. + * This element allows for the linked list algorithm by always holding a + * reference to the next element in the list. + * if Element.next is null, then this element is the tail of the list. + */ + static class Element { + /*the actual value stored in the queue*/ + Object obj=null; + /*pointer to the next item in the (queue) linked list*/ + Element next=null; + + /** + * creates an Element object holding its value + * @param o - the object to be stored in the queue position + */ + Element(Object o) { + obj=o; + } + + /** + * prints out the value of the object + */ + public String toString() { + return obj != null? obj.toString() : "null"; + } + } + + + /** + * creates an empty queue + */ + public Queue() { + } + + + /** + * Returns the first element. Returns null if no elements are available. + */ + public Object getFirst() { + synchronized(mutex) { + return head != null? head.obj : null; + } + } + + /** + * Returns the last element. Returns null if no elements are available. + */ + public Object getLast() { + synchronized(mutex) { + return tail != null? tail.obj : null; + } + } + + + /** + * returns true if the Queue has been closed + * however, this method will return false if the queue has been closed + * using the close(true) method and the last element has yet not been received. + * @return true if the queue has been closed + */ + public boolean closed() { + synchronized(mutex) { + return closed; + } + } + + /** + * adds an object to the tail of this queue + * If the queue has been closed with close(true) no exception will be + * thrown if the queue has not been flushed yet. + * @param obj - the object to be added to the queue + * @exception QueueClosedException exception if closed() returns true + */ + public void add(Object obj) throws QueueClosedException { + if(obj == null) { + if(log.isErrorEnabled()) log.error("argument must not be null"); + return; + } + + /*lock the queue from other threads*/ + synchronized(mutex) { + if(closed) + throw new QueueClosedException(); + if(this.num_markers > 0) + throw new QueueClosedException("queue has been closed. You can not add more elements. " + + "Waiting for removal of remaining elements."); + addInternal(obj); + + /*wake up all the threads that are waiting for the lock to be released*/ + mutex.notifyAll(); + } + } + + public void addAll(Collection c) throws QueueClosedException { + if(c == null) { + if(log.isErrorEnabled()) log.error("argument must not be null"); + return; + } + + /*lock the queue from other threads*/ + synchronized(mutex) { + if(closed) + throw new QueueClosedException(); + if(this.num_markers > 0) + throw new QueueClosedException("queue has been closed. You can not add more elements. " + + "Waiting for removal of remaining elements."); + + Object obj; + for(Iterator it=c.iterator(); it.hasNext();) { + obj=it.next(); + if(obj != null) + addInternal(obj); + } + + /*wake up all the threads that are waiting for the lock to be released*/ + mutex.notifyAll(); + } + } + + + public void addAll(List list) throws QueueClosedException { + if(list == null) { + if(log.isErrorEnabled()) log.error("argument must not be null"); + return; + } + + /*lock the queue from other threads*/ + synchronized(mutex) { + if(closed) + throw new QueueClosedException(); + if(this.num_markers > 0) + throw new QueueClosedException("queue has been closed. You can not add more elements. " + + "Waiting for removal of remaining elements."); + + for(Object obj: list) { + if(obj != null) + addInternal(obj); + } + + /*wake up all the threads that are waiting for the lock to be released*/ + mutex.notifyAll(); + } + } + + + + + /** + * Adds a new object to the head of the queue + * basically (obj.equals(queue.remove(queue.add(obj)))) returns true + * If the queue has been closed with close(true) no exception will be + * thrown if the queue has not been flushed yet. + * @param obj - the object to be added to the queue + * @exception QueueClosedException exception if closed() returns true + */ + public void addAtHead(Object obj) throws QueueClosedException { + if(obj == null) { + if(log.isErrorEnabled()) log.error("argument must not be null"); + return; + } + + /*lock the queue from other threads*/ + synchronized(mutex) { + if(closed) + throw new QueueClosedException(); + if(this.num_markers > 0) + throw new QueueClosedException("Queue.addAtHead(): queue has been closed. You can not add more elements. " + + "Waiting for removal of remaining elements."); + + Element el=new Element(obj); + /*check the head element in the list*/ + if(head == null) { + /*this is the first object, we could have done add(obj) here*/ + head=el; + tail=head; + size=1; + } + else { + /*set the head element to be the child of this one*/ + el.next=head; + /*set the head to point to the recently added object*/ + head=el; + /*increase the size*/ + size++; + } + /*wake up all the threads that are waiting for the lock to be released*/ + mutex.notifyAll(); + } + } + + + /** + * Removes 1 element from head or blocks + * until next element has been added or until queue has been closed + * @return the first element to be taken of the queue + */ + public Object remove() throws QueueClosedException { + Object retval; + synchronized(mutex) { + /*wait as long as the queue is empty. return when an element is present or queue is closed*/ + while(size == 0) { + if(closed) + throw new QueueClosedException(); + try { + mutex.wait(); + } + catch(InterruptedException ex) { + } + } + + if(closed) + throw new QueueClosedException(); + + /*remove the head from the queue, if we make it to this point, retval should not be null !*/ + retval=removeInternal(); + if(retval == null) + if(log.isErrorEnabled()) log.error("element was null, should never be the case"); + } + + /* + * we ran into an Endmarker, which means that the queue was closed before + * through close(true) + */ +// if(retval == endMarker) { +// close(false); // mark queue as closed +// throw new QueueClosedException(); +// } + return retval; + } + + + /** + * Removes 1 element from the head. + * If the queue is empty the operation will wait for timeout ms. + * if no object is added during the timeout time, a Timout exception is thrown + * @param timeout - the number of milli seconds this operation will wait before it times out + * @return the first object in the queue + */ + public Object remove(long timeout) throws QueueClosedException, TimeoutException { + Object retval; + + synchronized(mutex) { + if(closed) + throw new QueueClosedException(); + + /*if the queue size is zero, we want to wait until a new object is added*/ + if(size == 0) { + try { + /*release the mutex lock and wait no more than timeout ms*/ + mutex.wait(timeout); + } + catch(InterruptedException ex) { + } + } + /*we either timed out, or got notified by the mutex lock object*/ + if(closed) + throw new QueueClosedException(); + + /*get the next value*/ + retval=removeInternal(); + /*null result means we timed out*/ + if(retval == null) throw new TimeoutException("timeout=" + timeout + "ms"); + + /*if we reached an end marker we are going to close the queue*/ +// if(retval == endMarker) { +// close(false); +// throw new QueueClosedException(); +// } + /*at this point we actually did receive a value from the queue, return it*/ + return retval; + } + } + + + /** + * removes a specific object from the queue. + * the object is matched up using the Object.equals method. + * @param obj the actual object to be removed from the queue + */ + public void removeElement(Object obj) throws QueueClosedException { + Element el, tmp_el; + + if(obj == null) { + if(log.isErrorEnabled()) log.error("argument must not be null"); + return; + } + + synchronized(mutex) { + if(closed) /*check to see if the queue is closed*/ + throw new QueueClosedException(); + + el=head; + + /*the queue is empty*/ + if(el == null) return; + + /*check to see if the head element is the one to be removed*/ + if(el.obj.equals(obj)) { + /*the head element matched we will remove it*/ + head=el.next; + el.next=null; + el.obj=null; + /*check if we only had one object left + *at this time the queue becomes empty + *this will set the tail=head=null + */ + if(size == 1) + tail=head; // null + decrementSize(); + return; + } + + /*look through the other elements*/ + while(el.next != null) { + if(el.next.obj.equals(obj)) { + tmp_el=el.next; + if(tmp_el == tail) // if it is the last element, move tail one to the left (bela Sept 20 2002) + tail=el; + el.next.obj=null; + el.next=el.next.next; // point to the el past the next one. can be null. + tmp_el.next=null; + tmp_el.obj=null; + decrementSize(); + break; + } + el=el.next; + } + } + } + + + /** + * returns the first object on the queue, without removing it. + * If the queue is empty this object blocks until the first queue object has + * been added + * @return the first object on the queue + */ + public Object peek() throws QueueClosedException { + Object retval; + + synchronized(mutex) { + while(size == 0) { + if(closed) + throw new QueueClosedException(); + try { + mutex.wait(); + } + catch(InterruptedException ex) { + } + } + + if(closed) + throw new QueueClosedException(); + + retval=(head != null)? head.obj : null; + } + + if(retval == endMarker) { + close(false); // mark queue as closed + throw new QueueClosedException(); + } + + return retval; + } + + + /** + * returns the first object on the queue, without removing it. + * If the queue is empty this object blocks until the first queue object has + * been added or the operation times out + * @param timeout how long in milli seconds will this operation wait for an object to be added to the queue + * before it times out + * @return the first object on the queue + */ + public Object peek(long timeout) throws QueueClosedException, TimeoutException { + Object retval; + + synchronized(mutex) { + if(size == 0) { + if(closed) + throw new QueueClosedException(); + try { + mutex.wait(timeout); + } + catch(InterruptedException ex) { + } + } + if(closed) + throw new QueueClosedException(); + + retval=head != null? head.obj : null; + + if(retval == null) throw new TimeoutException("timeout=" + timeout + "ms"); + + if(retval == endMarker) { + close(false); + throw new QueueClosedException(); + } + return retval; + } + } + + /** Removes all elements from the queue. This method can succeed even when the queue is closed */ + public void clear() { + synchronized(mutex) { + head=tail=null; + size=0; + num_markers=0; + mutex.notifyAll(); + } + } + + + /** + Marks the queues as closed. When an add or remove operation is + attempted on a closed queue, an exception is thrown. + @param flush_entries When true, a end-of-entries marker is added to the end of the queue. + Entries may be added and removed, but when the end-of-entries marker + is encountered, the queue is marked as closed. This allows to flush + pending messages before closing the queue. + */ + public void close(boolean flush_entries) { + synchronized(mutex) { + if(flush_entries && size > 0) { + try { + add(endMarker); // add an end-of-entries marker to the end of the queue + num_markers++; + } + catch(QueueClosedException closed_ex) { + } + return; + } + closed=true; + mutex.notifyAll(); + } + } + + /** Waits until the queue has been closed. Returns immediately if already closed + * @param timeout Number of milliseconds to wait. A value <= 0 means to wait forever + */ + public void waitUntilClosed(long timeout) { + synchronized(mutex) { + if(closed) + return; + try { + mutex.wait(timeout); + } + catch(InterruptedException e) { + } + } + } + + + /** + * resets the queue. + * This operation removes all the objects in the queue and marks the queue open + */ + public void reset() { + synchronized(mutex) { + num_markers=0; + if(!closed) + close(false); + size=0; + head=null; + tail=null; + closed=false; + mutex.notifyAll(); + } + } + + /** + * Returns all the elements of the queue + * @return A copy of the queue + */ + public LinkedList values() { + LinkedList retval=new LinkedList(); + synchronized(mutex) { + Element el=head; + while(el != null) { + retval.add(el.obj); + el=el.next; + } + } + return retval; + } + + + /** + * returns the number of objects that are currently in the queue + */ + public int size() { + synchronized(mutex) { + return size - num_markers; + } + } + + /** + * prints the size of the queue + */ + public String toString() { + return "Queue (" + size() + ") elements"; + } + + + + + /* ------------------------------------- Private Methods ----------------------------------- */ + + + private final void addInternal(Object obj) { + /*create a new linked list element*/ + Element el=new Element(obj); + /*check the first element*/ + if(head == null) { + /*the object added is the first element*/ + /*set the head to be this object*/ + head=el; + /*set the tail to be this object*/ + tail=head; + /*set the size to be one, since the queue was empty*/ + size=1; + } + else { + /*add the object to the end of the linked list*/ + tail.next=el; + /*set the tail to point to the last element*/ + tail=el; + /*increase the size*/ + size++; + } + } + + /** + * Removes the first element. Returns null if no elements in queue. + * Always called with mutex locked (we don't have to lock mutex ourselves) + */ + private Object removeInternal() { + Element retval; + Object obj; + + /*if the head is null, the queue is empty*/ + if(head == null) + return null; + + retval=head; // head must be non-null now + + head=head.next; + if(head == null) + tail=null; + + decrementSize(); + if(head != null && head.obj == endMarker) { + closed=true; + mutex.notifyAll(); + } + + retval.next=null; + obj=retval.obj; + retval.obj=null; + return obj; + } + + + /** Doesn't need to be synchronized; is always called from synchronized methods */ + final private void decrementSize() { + size--; + if(size < 0) + size=0; + } + + + /* ---------------------------------- End of Private Methods -------------------------------- */ + +} Index: 3rdParty_sources/jgroups/org/jgroups/util/Queue.java.concurrent =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/Queue.java.concurrent,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/Queue.java.concurrent 17 Aug 2012 14:51:02 -0000 1.1 @@ -0,0 +1,113 @@ +// $Id: Queue.java.concurrent,v 1.1 2012/08/17 14:51:02 marcin Exp $ + +package org.jgroups.util; + + +import org.jgroups.TimeoutException; +import org.jgroups.log.Trace; + +import java.util.Vector; + +import EDU.oswego.cs.dl.util.concurrent.WaitFreeQueue; + + + + +/** + * Elements are added at the tail and removed from the head. Class is thread-safe in that + * 1 producer and 1 consumer may add/remove elements concurrently. The class is not + * explicitely designed for multiple producers or consumers. Implemented as a linked + * list, so that removal of an element at the head does not cause a right-shift of the + * remaining elements (as in a Vector-based implementation). + * @author Bela Ban + * @author Filip Hanik + */ +public class Queue extends WaitFreeQueue { + boolean closed=false; + int size=0; + + + public Queue() { + } + + public Object getFirst() { + return super.peek(); + } + + public Object getLast() { + return null; // not implemented + } + + public boolean closed() { + return closed; + } + + public void add(Object obj) throws QueueClosedException { + try { + super.put(obj); + size++; + } + catch(InterruptedException e) { + e.printStackTrace(); + } + } + + public void addAtHead(Object obj) throws QueueClosedException { + try { + super.put(obj); + size++; + } + catch(InterruptedException e) { + e.printStackTrace(); + } + } + + public Object remove() throws QueueClosedException { + Object retval=null; + try { + retval=super.take(); + size--; + return retval; + } + catch(InterruptedException e) { + e.printStackTrace(); + return retval; + } + } + + public Object remove(long timeout) throws QueueClosedException, TimeoutException { + return remove(); + } + + public void removeElement(Object obj) throws QueueClosedException { + ; + } + + public Object peek() { + return super.peek(); + } + + public Object peek(long timeout) throws QueueClosedException, TimeoutException { + return peek(); + } + + public void close(boolean flush_entries) { + closed=true; + } + + public void reset() { + closed=false; + size=0; + } + + public int size() { + return size; + } + + public String toString() { + return super.toString(); + } + + + +} Index: 3rdParty_sources/jgroups/org/jgroups/util/QueueClosedException.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/QueueClosedException.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/QueueClosedException.java 17 Aug 2012 14:51:00 -0000 1.1 @@ -0,0 +1,25 @@ +// $Id: QueueClosedException.java,v 1.1 2012/08/17 14:51:00 marcin Exp $ + +package org.jgroups.util; + + +public class QueueClosedException extends Exception { + + private static final long serialVersionUID = -7575787375592873964L; + + public QueueClosedException() { + + } + + public QueueClosedException( String msg ) + { + super( msg ); + } + + public String toString() { + if ( this.getMessage() != null ) + return "QueueClosedException: " + this.getMessage(); + else + return "QueueClosedException"; + } +} Index: 3rdParty_sources/jgroups/org/jgroups/util/Range.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/Range.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/Range.java 17 Aug 2012 14:51:01 -0000 1.1 @@ -0,0 +1,53 @@ +// $Id: Range.java,v 1.1 2012/08/17 14:51:01 marcin Exp $ + +package org.jgroups.util; + + +import java.io.*; + + + +public class Range implements Externalizable, Streamable { + public long low=-1; // first msg to be retransmitted + public long high=-1; // last msg to be retransmitted + + + + /** For externalization */ + public Range() { + } + + public Range(long low, long high) { + this.low=low; this.high=high; + } + + + + public String toString() { + return "[" + low + " : " + high + ']'; + } + + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeLong(low); + out.writeLong(high); + } + + + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + low=in.readLong(); + high=in.readLong(); + } + + + public void writeTo(DataOutputStream out) throws IOException { + out.writeLong(low); + out.writeLong(high); + } + + public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + low=in.readLong(); + high=in.readLong(); + } +} Index: 3rdParty_sources/jgroups/org/jgroups/util/ReusableThread.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/ReusableThread.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/ReusableThread.java 17 Aug 2012 14:51:02 -0000 1.1 @@ -0,0 +1,279 @@ +// $Id: ReusableThread.java,v 1.1 2012/08/17 14:51:02 marcin Exp $ + +package org.jgroups.util; + + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + + +/** + * Reusable thread class. Instead of creating a new thread per task, this instance can be reused + * to run different tasks in turn. This is done by looping and assigning the Runnable task objects + * whose run method is then called.
        + * Tasks are Runnable objects and should be prepared to terminate when they receive an + * InterruptedException. This is thrown by the stop() method.
        + *

        + * The following situations have to be tested: + *

          + *
        1. ReusableThread is started. Then, brefore assigning a task, it is stopped again + *
        2. ReusableThread is started, assigned a long running task. Then, before task is done, + * stop() is called + *
        3. ReusableThread is started, assigned a task. Then waitUntilDone() is called, then stop() + *
        4. ReusableThread is started, assigned a number of tasks (waitUntilDone() called between tasks), + * then stopped + *
        5. ReusableThread is started, assigned a task + *
        + * + * @author Bela Ban + */ +public class ReusableThread implements Runnable { + volatile Thread thread=null; // thread that works on the task + Runnable task=null; // task assigned to thread + String thread_name="ReusableThread"; + volatile boolean suspended=false; + protected static final Log log=LogFactory.getLog(ReusableThread.class); + final long TASK_JOIN_TIME=3000; // wait 3 secs for an interrupted thread to terminate + + + public ReusableThread() { + } + + + public ReusableThread(String thread_name) { + this.thread_name=thread_name; + } + + + public boolean done() { + return task == null; + } + + public boolean available() { + return done(); + } + + public boolean isAlive() { + synchronized(this) { + return thread != null && thread.isAlive(); + } + } + + + /** + * Will always be called from synchronized method, no need to do our own synchronization + */ + public void start() { + if(thread == null || (thread != null && !thread.isAlive())) { + thread=new Thread(this, thread_name); + thread.setDaemon(true); + thread.start(); + } + } + + + /** + * Stops the thread by setting thread=null and interrupting it. The run() method catches the + * InterruptedException and checks whether thread==null. If this is the case, it will terminate + */ + public void stop() { + Thread tmp=null; + + if(log.isTraceEnabled()) log.trace("entering THIS"); + synchronized(this) { + if(log.isTraceEnabled()) + log.trace("entered THIS (thread=" + printObj(thread) + + ", task=" + printObj(task) + ", suspended=" + suspended + ')'); + if(thread != null && thread.isAlive()) { + tmp=thread; + thread=null; // signals the thread to stop + task=null; + if(log.isTraceEnabled()) log.trace("notifying thread"); + notifyAll(); + if(log.isTraceEnabled()) log.trace("notifying thread completed"); + } + thread=null; + task=null; + } + + if(tmp != null && tmp.isAlive()) { + long s1=System.currentTimeMillis(), s2=0; + if(log.isTraceEnabled()) log.trace("join(" + TASK_JOIN_TIME + ')'); + + tmp.interrupt(); + + try { + tmp.join(TASK_JOIN_TIME); + } + catch(Exception e) { + } + s2=System.currentTimeMillis(); + if(log.isTraceEnabled()) log.trace("join(" + TASK_JOIN_TIME + ") completed in " + (s2 - s1)); + if(tmp.isAlive()) + if(log.isErrorEnabled()) log.error("thread is still alive"); + tmp=null; + } + } + + + /** + * Suspends the thread. Does nothing if already suspended. If a thread is waiting to be assigned a task, or + * is currently running a (possibly long-running) task, then it will be suspended the next time it + * waits for suspended==false (second wait-loop in run()) + */ + + public void suspend() { + synchronized(this) { + if(log.isTraceEnabled()) log.trace("suspended=" + suspended + ", task=" + printObj(task)); + if(suspended) + return; // already suspended + else + suspended=true; + } + } + + + /** + * Resumes the thread. Noop if not suspended + */ + public void resume() { + synchronized(this) { + suspended=false; + notifyAll(); // notifies run(): the wait on suspend() is released + } + } + + + /** + * Assigns a task to the thread. If the thread is not running, it will be started. It it is + * already working on a task, it will reject the new task. Returns true if task could be + * assigned auccessfully + */ + public boolean assignTask(Runnable t) { + synchronized(this) { + start(); // creates and starts the thread if not yet running + if(task == null) { + task=t; + notifyAll(); // signals run() to start working (first wait-loop) + return true; + } + else { + if(log.isErrorEnabled()) + log.error("already working on a thread: current_task=" + task + ", new task=" + t + + ", thread=" + thread + ", is alive=" + (thread != null ? "" + thread.isAlive() : "null")); + return false; + } + } + } + + + /** + * Delicate piece of code (means very important :-)). Works as follows: loops until stop is true. + * Waits in a loop until task is assigned. Then runs the task and notifies waiters that it's done + * when task is completed. Then returns to the first loop to wait for more work. Does so until + * stop() is called, which sets stop=true and interrupts the thread. If waiting for a task, the + * thread terminates. If running a task, the task is interrupted, and the thread terminates. If the + * task is not interrupible, the stop() method will wait for 3 secs (join on the thread), then return. + * This means that the run() method of the task will complete and only then will the thread be + * garbage-collected. + */ + public void run() { + while(thread != null) { // Stop sets thread=null + try { + if(log.isTraceEnabled()) log.trace("entering ASSIGN"); + synchronized(this) { + if(log.isTraceEnabled()) + log.trace("entered ASSIGN (task=" + printObj(task) + ", thread=" + printObj(thread) + ')'); + + while(task == null && thread != null) { // first wait-loop: wait for task to be assigned (assignTask()) + if(log.isTraceEnabled()) log.trace("wait ASSIGN"); + wait(); + if(log.isTraceEnabled()) log.trace("wait ASSIGN completed"); + } + } + } + catch(InterruptedException ex) { // on assignTask() + if(log.isTraceEnabled()) log.trace("interrupt on ASSIGN"); + } + if(thread == null) return; // we need to terminate + + try { + if(log.isTraceEnabled()) log.trace("entering SUSPEND"); + synchronized(this) { + if(log.isTraceEnabled()) + log.trace("entered SUSPEND (suspended=" + suspended + ", task=" + printObj(task) + ')'); + while(suspended && thread != null) { // second wait-loop: wait for thread to resume (resume()) + if(log.isTraceEnabled()) log.trace("wait SUSPEND"); + wait(); + if(log.isTraceEnabled()) log.trace("wait SUSPEND completed"); + } + } + } + catch(InterruptedException ex) { // on resume() + if(log.isTraceEnabled()) log.trace("interrupt on RESUME"); + } + if(thread == null) return; // we need to terminate + + if(task != null) { + if(log.isTraceEnabled()) log.trace("running task"); + try { + task.run(); //here we are actually running the task + } + catch(Throwable ex) { + if(log.isErrorEnabled()) log.error("failed running task", ex); + } + if(log.isTraceEnabled()) log.trace("task completed"); + } + + if(log.isTraceEnabled()) log.trace("entering THIS"); + synchronized(this) { + if(log.isTraceEnabled()) log.trace("entered THIS"); + task=null; + if(log.isTraceEnabled()) log.trace("notify THIS"); + notifyAll(); + if(log.isTraceEnabled()) log.trace("notify THIS completed"); + } + } + if(log.isTraceEnabled()) log.trace("terminated"); + } + + + String printObj(Object obj) { + if(obj == null) + return "null"; + else + return "non-null"; + } + + + public void waitUntilDone() { + + if(log.isTraceEnabled()) log.trace("entering THIS"); + synchronized(this) { + if(log.isTraceEnabled()) log.trace("entered THIS (task=" + printObj(task) + ')'); + while(task != null) { + try { + if(log.isTraceEnabled()) log.trace("wait THIS"); + wait(); + if(log.isTraceEnabled()) log.trace("wait THIS completed"); + } + catch(InterruptedException interrupted) { + } + } + } + } + + + public String toString() { + return "suspended=" + suspended; + } + + +} + + + + + + + Index: 3rdParty_sources/jgroups/org/jgroups/util/Rsp.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/Rsp.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/Rsp.java 17 Aug 2012 14:51:01 -0000 1.1 @@ -0,0 +1,86 @@ +// $Id: Rsp.java,v 1.1 2012/08/17 14:51:01 marcin Exp $ + +package org.jgroups.util; + +import org.jgroups.Address; + + +/** + * class that represents a response from a communication + */ +public class Rsp { + /* flag that represents whether the response was received */ + boolean received=false; + + /* flag that represents whether the response was suspected */ + boolean suspected=false; + + /* The sender of this response */ + Address sender=null; + + /* the value from the response */ + Object retval=null; + + + public Rsp(Address sender) { + this.sender=sender; + } + + public Rsp(Address sender, boolean suspected) { + this.sender=sender; + this.suspected=suspected; + } + + public Rsp(Address sender, Object retval) { + this.sender=sender; + this.retval=retval; + received=true; + } + + public boolean equals(Object obj) { + if(!(obj instanceof Rsp)) + return false; + Rsp other=(Rsp)obj; + if(sender != null) + return sender.equals(other.sender); + return other.sender == null; + } + + public Object getValue() { + return retval; + } + + public void setValue(Object val) { + this.retval=val; + } + + public Address getSender() { + return sender; + } + + public boolean wasReceived() { + return received; + } + + public void setReceived(boolean received) { + this.received=received; + if(received) + suspected=false; + } + + public boolean wasSuspected() { + return suspected; + } + + public void setSuspected(boolean suspected) { + this.suspected=suspected; + if(suspected) + received=false; + } + + public String toString() { + return new StringBuilder("sender=").append(sender).append(", retval=").append(retval).append(", received="). + append(received).append(", suspected=").append(suspected).toString(); + } +} + Index: 3rdParty_sources/jgroups/org/jgroups/util/RspList.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/RspList.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/RspList.java 17 Aug 2012 14:51:01 -0000 1.1 @@ -0,0 +1,237 @@ +// $Id: RspList.java,v 1.1 2012/08/17 14:51:01 marcin Exp $ + +package org.jgroups.util; + + +import org.jgroups.Address; + +import java.util.*; + + +/** + * Contains responses from all members. Marks faulty members. + * A RspList is a response list used in peer-to-peer protocols. This class is unsynchronized + */ +public class RspList implements Map { + + /** Map */ + final Map rsps=new HashMap(); + + public RspList() { + + } + + /** Adds a list of responses + * @param responses Collection + */ + public RspList(Collection responses) { + if(responses != null) { + for(Rsp rsp: responses) { + rsps.put(rsp.getSender(), rsp); + } + } + } + + + public boolean isEmpty() { + return rsps.isEmpty(); + } + + public boolean containsKey(Object key) { + return rsps.containsKey(key); + } + + public boolean containsValue(Object value) { + return rsps.containsValue(value); + } + + /** + * Returns the Rsp associated with address key + * @param key Address (key) + * @return Rsp + */ + public Rsp get(Object key) { + return rsps.get(key); + } + + /** + * Returns the value associated with address key + * @param key + * @return Object value + */ + public Object getValue(Object key) { + Rsp rsp=get(key); + return rsp != null? rsp.getValue() : null; + } + + public Rsp put(Address key, Rsp value) { + return rsps.put(key, value); + } + + public Rsp remove(Object key) { + return rsps.remove(key); + } + + public void putAll(Map m) { + rsps.putAll(m); + } + + public void clear() { + rsps.clear(); + } + + public Set
        keySet() { + return rsps.keySet(); + } + + public Collection values() { + return rsps.values(); + } + + public Set> entrySet() { + return rsps.entrySet(); + } + + /** + * Clears the response list + * @deprecated Use {@link #clear()} instead + */ + public void reset() { + clear(); + } + + + public void addRsp(Address sender, Object retval) { + Rsp rsp=get(sender); + if(rsp != null) { + rsp.sender=sender; + rsp.retval=retval; + rsp.received=true; + rsp.suspected=false; + return; + } + rsps.put(sender, new Rsp(sender, retval)); + } + + + public void addNotReceived(Address sender) { + Rsp rsp=get(sender); + if(rsp == null) + rsps.put(sender, new Rsp(sender)); + } + + + public void addSuspect(Address sender) { + Rsp rsp=get(sender); + if(rsp != null) { + rsp.sender=sender; + rsp.retval=null; + rsp.received=false; + rsp.suspected=true; + return; + } + rsps.put(sender, new Rsp(sender, true)); + } + + + public boolean isReceived(Address sender) { + Rsp rsp=get(sender); + return rsp != null && rsp.received; + } + + public int numSuspectedMembers() { + int num=0; + Collection values=values(); + for(Rsp rsp: values) { + if(rsp.wasSuspected()) + num++; + } + return num; + } + + public int numReceived() { + int num=0; + Collection values=values(); + for(Rsp rsp: values) { + if(rsp.wasReceived()) + num++; + } + return num; + } + + /** Returns the first value in the response set. This is random, but we try to return a non-null value first */ + public Object getFirst() { + Collection values=values(); + for(Rsp rsp: values) { + if(rsp.getValue() != null) + return rsp.getValue(); + } + return null; + } + + + /** + * Returns the results from non-suspected members that are not null. + */ + public Vector getResults() { + Vector ret=new Vector(); + Object val; + + for(Rsp rsp: values()) { + if(rsp.wasReceived() && (val=rsp.getValue()) != null) + ret.addElement(val); + } + return ret; + } + + + public Vector
        getSuspectedMembers() { + Vector
        retval=new Vector
        (); + for(Rsp rsp: values()) { + if(rsp.wasSuspected()) + retval.addElement(rsp.getSender()); + } + return retval; + } + + + public boolean isSuspected(Address sender) { + Rsp rsp=get(sender); + return rsp != null && rsp.suspected; + } + + + public int size() { + return rsps.size(); + } + + /** + * Returns the Rsp at index i + * @param i The index + * @return a Rsp + * @throws ArrayIndexOutOfBoundsException + * @deprecated Use {@link #entrySet()} or {@link #values()} instead + */ + public Object elementAt(int i) throws ArrayIndexOutOfBoundsException { + Set
        keys=new TreeSet
        (keySet()); + Object[] keys_array=keys.toArray(); + Object key=keys_array[i]; + return get(key); + } + + + public String toString() { + StringBuilder ret=new StringBuilder(); + for(Rsp rsp: values()) { + ret.append("[" + rsp + "]\n"); + } + return ret.toString(); + } + + + boolean contains(Address sender) { + return containsKey(sender); + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/util/Scheduler.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/Scheduler.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/Scheduler.java 17 Aug 2012 14:51:02 -0000 1.1 @@ -0,0 +1,240 @@ +// $Id: Scheduler.java,v 1.1 2012/08/17 14:51:02 marcin Exp $ + +package org.jgroups.util; + + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.Global; + + +/** + * Implementation of a priority scheduler. The scheduler maintains a queue to the end of which + * all tasks are added. It continually looks at the first queue element, assigns a thread to + * it, runs the thread and waits for completion. When a new priority task is added, + * it will be added to the head of the queue and the scheduler will be interrupted. In this + * case, the currently handled task is suspended, and the one at the head of the queue + * handled. This is recursive: a priority task can always be interrupted by another priority + * task. Recursion ends when no more priority tasks are added, or when the thread pool is + * exhausted. + * + * @author Bela Ban + */ +public class Scheduler implements Runnable { + final Queue queue=new Queue(); + Thread sched_thread=null; + Task current_task=null; + ThreadPool pool=null; + SchedulerListener listener=null; + + protected static final Log log=LogFactory.getLog(Scheduler.class); + + /** Process items on the queue concurrently. The default is to wait until the processing of an item + * has completed before fetching the next item from the queue. Note that setting this to true + * may destroy the properties of a protocol stack, e.g total or causal order may not be + * guaranteed. Set this to true only if you know what you're doing ! */ + boolean concurrent_processing=false; + + /** max number of threads, will only be allocated when needed */ + int NUM_THREADS=128; + + static final int WAIT_FOR_THREAD_AVAILABILITY=3000; + + + + + + + public Scheduler() { + String tmp=Util.getProperty(new String[]{Global.SCHEDULER_MAX_THREADS}, null, null, false, "128"); + this.NUM_THREADS=Integer.parseInt(tmp); + } + + + public Scheduler(int num_threads) { + this.NUM_THREADS=num_threads; + } + + + public void setListener(SchedulerListener l) { + listener=l; + } + + + public boolean getConcurrentProcessing() { + return concurrent_processing; + } + + public void setConcurrentProcessing(boolean process_concurrently) { + this.concurrent_processing=process_concurrently; + } + + public void run() { + while(sched_thread != null) { + if(queue.closed()) break; + try { + current_task=(Task)queue.peek(); // get the first task in the queue (blocks until available) + if(current_task == null) { // @remove + if(log.isWarnEnabled()) log.warn("current task is null, queue.size()=" + queue.size() + + ", queue.closed()=" + queue.closed() + ", continuing"); + continue; + } + + if(current_task.suspended) { + current_task.suspended=false; + current_task.thread.resume(); + if(listener != null) listener.resumed(current_task.target); + } + else { + if(current_task.thread == null) { + current_task.thread=pool.getThread(); + if(current_task.thread == null) { // thread pool exhausted + if(log.isWarnEnabled()) log.warn("thread pool exhausted, waiting for " + + WAIT_FOR_THREAD_AVAILABILITY + "ms before retrying"); + Util.sleep(WAIT_FOR_THREAD_AVAILABILITY); + continue; + } + } + + // if we get here, current_task.thread and current_task.target are guaranteed to be non-null + if(listener != null) listener.started(current_task.target); + if(current_task.thread.assignTask(current_task.target) == false) + continue; + } + + /* + * http://jira.jboss.com/jira/browse/JGRP-690 + * Thread interrupt status is not always cleared by default + */ + if(Thread.interrupted()) + throw new InterruptedException(); + + if(concurrent_processing == false) { // this is the default: process serially + synchronized(current_task.thread) { + while(!current_task.thread.done() && !current_task.thread.suspended) + current_task.thread.wait(); + } + if(listener != null) listener.stopped(current_task.target); + } + queue.removeElement(current_task); + } + catch(InterruptedException interrupted) { + if(sched_thread == null || queue.closed()) break; + if(current_task.thread != null) { + current_task.thread.suspend(); + if(listener != null) listener.suspended(current_task.target); + current_task.suspended=true; + } + } + catch(QueueClosedException closed_ex) { + return; + } + catch(Throwable ex) { + if(log.isErrorEnabled()) log.error("exception=" + Util.print(ex)); + } + } + if(log.isTraceEnabled()) log.trace("scheduler thread terminated"); + } + + + public void addPrio(Runnable task) { + Task new_task=new Task(task); + boolean do_interrupt=false; + + try { + synchronized(queue) { // sync against add() + if(queue.size() == 0) + queue.add(new_task); + else { + queue.addAtHead(new_task); + do_interrupt=true; + } + } + if(do_interrupt) // moved out of 'synchronized(queue)' to minimize lock contention + sched_thread.interrupt(); + } + catch(Throwable e) { + if(log.isErrorEnabled()) log.error("exception=" + e); + } + } + + + public void add(Runnable task) { + Task new_task=new Task(task); + + try { + synchronized(queue) { // sync against addPrio() + queue.add(new_task); + } + } + catch(Exception e) { + if(log.isErrorEnabled()) log.error("exception=" + e); + } + } + + + public void start() { + if(queue.closed()) + queue.reset(); + if(sched_thread == null) { + pool=new ThreadPool(NUM_THREADS); + sched_thread=new Thread(this, "Scheduler main thread"); + sched_thread.setDaemon(true); + sched_thread.start(); + } + } + + + /** + * Stop the scheduler thread. The thread may be waiting for its next task (queue.peek()) or it may be waiting on + * the currently executing thread. In the first case, closing the queue will throw a QueueClosed exception which + * terminates the scheduler thread. In the second case, after closing the queue, we interrupt the scheduler thread, + * which then checks whether the queue is closed. If this is the case, the scheduler thread terminates. + */ + public void stop() { + Thread tmp=null; + + // 1. Close the queue + queue.close(false); // will stop thread at next peek(); + + // 2. Interrupt the scheduler thread + if(sched_thread != null && sched_thread.isAlive()) { + tmp=sched_thread; + sched_thread=null; + tmp.interrupt(); + try { + tmp.join(Global.THREAD_SHUTDOWN_WAIT_TIME); + } + catch(InterruptedException e) { + Thread.currentThread().interrupt(); // set interrupt flag again + } + if(tmp.isAlive()) + if(log.isErrorEnabled()) log.error("scheduler thread is still not dead !!!"); + } + sched_thread=null; + + // 3. Delete the thread pool + if(pool != null) { + pool.destroy(); + pool=null; + } + } + + + + public static class Task { + ReusableThread thread=null; + Runnable target=null; + boolean suspended=false; + + Task(Runnable target) { + this.target=target; + } + + public String toString() { + return "[thread=" + thread + ", target=" + target + ", suspended=" + suspended + ']'; + } + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/util/SchedulerListener.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/SchedulerListener.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/SchedulerListener.java 17 Aug 2012 14:51:02 -0000 1.1 @@ -0,0 +1,25 @@ +// $Id: SchedulerListener.java,v 1.1 2012/08/17 14:51:02 marcin Exp $ + +package org.jgroups.util; + +/** + * Provides callback for use with a {@link Scheduler}. + */ +public interface SchedulerListener { + /** + * @param r + */ + void started(Runnable r); + /** + * @param r + */ + void stopped(Runnable r); + /** + * @param r + */ + void suspended(Runnable r); + /** + * @param r + */ + void resumed(Runnable r); +} Index: 3rdParty_sources/jgroups/org/jgroups/util/SeqnoTable.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/SeqnoTable.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/SeqnoTable.java 17 Aug 2012 14:51:00 -0000 1.1 @@ -0,0 +1,115 @@ +package org.jgroups.util; + +import org.jgroups.Address; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * Maintains the highest received and highest delivered seqno per member + * @author Bela Ban + * @version $Id: SeqnoTable.java,v 1.1 2012/08/17 14:51:00 marcin Exp $ + */ +public class SeqnoTable { + private long next_to_receive=0; + private final ConcurrentMap map=new ConcurrentHashMap(); + + + public SeqnoTable(long next_to_receive) { + this.next_to_receive=next_to_receive; + } + + public long getHighestReceived(Address member) { + Entry entry=map.get(member); + return entry != null? entry.getHighestReceived() : -1; + } + + public long getNextToReceive(Address member) { + Entry entry=map.get(member); + return entry != null? entry.getNextToReceive() : -1; + } + + public boolean add(Address member, long seqno) { + Entry entry=map.get(member); + if(entry == null) { + entry=new Entry(next_to_receive); + Entry entry2=map.putIfAbsent(member, entry); + if(entry2 != null) + entry=entry2; + } + // now entry is not null + return entry.add(seqno); + } + + public void remove(Address member) { + map.remove(member); + } + + public boolean retainAll(Collection
        members) { + return map.keySet().retainAll(members); + } + + public void clear() { + map.clear(); + } + + public String toString() { + return map.toString(); + } + + + private static class Entry { + long highest_received; + long next_to_receive; + final Set seqnos=new HashSet(); + + private Entry(long initial_seqno) { + this.next_to_receive=this.highest_received=initial_seqno; + } + + public synchronized long getHighestReceived() { + return highest_received; + } + + public synchronized long getNextToReceive() { + return next_to_receive; + } + + public synchronized boolean add(long seqno) { + try { + if(seqno == next_to_receive) { + next_to_receive++; + while(true) { + if(seqnos.remove(next_to_receive)) { + next_to_receive++; + } + else + break; + } + return true; + } + + if(seqno < next_to_receive) + return false; + + // seqno > next_to_receive + seqnos.add(seqno); + return true; + } + finally { + highest_received=Math.max(highest_received, seqno); + } + } + + public String toString() { + StringBuilder sb=new StringBuilder(); + sb.append(next_to_receive).append(" - ").append(highest_received); + if(!seqnos.isEmpty()) + sb.append(" ").append(seqnos); + return sb.toString(); + } + } +} Index: 3rdParty_sources/jgroups/org/jgroups/util/ShutdownRejectedExecutionHandler.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/ShutdownRejectedExecutionHandler.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/ShutdownRejectedExecutionHandler.java 17 Aug 2012 14:51:02 -0000 1.1 @@ -0,0 +1,42 @@ +package org.jgroups.util; + +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.ThreadPoolExecutor; +import org.apache.commons.logging.LogFactory; + +/** + * ShutdownRejectedExecutionHandler is a decorator RejectedExecutionHandler used + * in all JGroups ThreadPoolExecutor(s). Default RejectedExecutionHandler raises + * RuntimeException when a task is submitted to ThreadPoolExecutor that has been + * shutdown. ShutdownRejectedExecutionHandler instead logs only a warning + * message. + * + * @author Vladimir Blagojevic + * @see ThreadPoolExecutor + * @see RejectedExecutionHandler + * @version $Id: ShutdownRejectedExecutionHandler.java,v 1.101 2008/04/08 + * 14:49:05 belaban Exp $ + */ +public class ShutdownRejectedExecutionHandler implements RejectedExecutionHandler { + + RejectedExecutionHandler handler; + + public ShutdownRejectedExecutionHandler(RejectedExecutionHandler handler) { + super(); + if(handler == null) + throw new NullPointerException("RejectedExecutionHandler cannot be null"); + this.handler=handler; + } + + public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { + + if(executor.isShutdown()) { + LogFactory.getLog(this.getClass()).warn("ThreadPoolExecutor " + executor + + " is shutdown and rejected submitted task " + + r); + } + else { + handler.rejectedExecution(r, executor); + } + } +} Index: 3rdParty_sources/jgroups/org/jgroups/util/SizeBoundedQueue.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/SizeBoundedQueue.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/SizeBoundedQueue.java 17 Aug 2012 14:51:00 -0000 1.1 @@ -0,0 +1,132 @@ +package org.jgroups.util; + + +import java.util.Map; +import java.util.Collection; +import java.util.Iterator; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.TimeUnit; + +/** + * Queue as described in http://jira.jboss.com/jira/browse/JGRP-376. However, this queue only works with + * {@link org.jgroups.protocols.TP.IncomingPacket} elements. + * The queue maintains a max number of bytes and a total of all of the messages in the internal queues. Whenever a + * message is added, we increment the total by the length of the message. When a message is removed, we decrement the + * total. Removal blocks until a message is available, addition blocks if the max size has been exceeded, until there + * is enough space to add another message. + * Note that the max size should always be greater than the size of the largest message to be received, otherwise an + * additon would always fail because msg.length > max size !
        + * Access patterns: this instance is always accessed by the thread pool only ! Concurrent take() or poll() methods, + * but only a single thread at a time calls put() ! + * @author Bela Ban + * @version $Id: SizeBoundedQueue.java,v 1.1 2012/08/17 14:51:00 marcin Exp $ + */ +public class SizeBoundedQueue implements BlockingQueue { + int max_size=1000 * 1000; + int capacity=0; + /** Map>. Maintains a list of unicast messages per sender */ + final Map ucast_msgs=new ConcurrentHashMap(10); + /** Map>. Maintains a list of multicast messages per sender */ + final Map mcast_msgs=new ConcurrentHashMap(10); + + + public boolean add(Object o) { + return false; + } + + public int drainTo(Collection c) { + return 0; + } + + public int drainTo(Collection c, int maxElements) { + return 0; + } + + public boolean offer(Object o) { + return false; + } + + public boolean offer(Object o, long timeout, TimeUnit unit) throws InterruptedException { + return false; + } + + public Object poll(long timeout, TimeUnit unit) throws InterruptedException { + return null; + } + + public void put(Object o) throws InterruptedException { + } + + public int remainingCapacity() { + return 0; + } + + public Object take() throws InterruptedException { + return null; + } + + + public Object element() { + return null; + } + + public Object peek() { + return null; + } + + public Object poll() { + return null; + } + + public Object remove() { + return null; + } + + public boolean addAll(Collection c) { + return false; + } + + public void clear() { + } + + public boolean contains(Object o) { + return false; + } + + public boolean containsAll(Collection c) { + return false; + } + + public boolean isEmpty() { + return false; + } + + public Iterator iterator() { + return null; + } + + public boolean remove(Object o) { + return false; + } + + public boolean removeAll(Collection c) { + return false; + } + + public boolean retainAll(Collection c) { + return false; + } + + public int size() { + return 0; + } + + public Object[] toArray() { + return new Object[0]; + } + + public Object[] toArray(Object[] a) { + return new Object[0]; + } +} Index: 3rdParty_sources/jgroups/org/jgroups/util/Streamable.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/Streamable.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/Streamable.java 17 Aug 2012 14:51:02 -0000 1.1 @@ -0,0 +1,22 @@ +package org.jgroups.util; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * Implementations of Streamable can add their state directly to the output stream, enabling them to bypass costly + * serialization + * @author Bela Ban + * @version $Id: Streamable.java,v 1.1 2012/08/17 14:51:02 marcin Exp $ + */ +public interface Streamable { + + /** Write the entire state of the current object (including superclasses) to outstream. + * Note that the output stream must not be closed */ + void writeTo(DataOutputStream out) throws IOException; + + /** Read the state of the current object (including superclasses) from instream + * Note that the input stream must not be closed */ + void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException; +} Index: 3rdParty_sources/jgroups/org/jgroups/util/ThreadDecorator.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/ThreadDecorator.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/ThreadDecorator.java 17 Aug 2012 14:51:02 -0000 1.1 @@ -0,0 +1,23 @@ +package org.jgroups.util; + +/** + * An object that can alter the state of a thread when it receives a callback from a {@link ThreadManager} notifying + * it that the thread has been created or released from use. + * + * @author Brian Stansberry + * @version $Id: ThreadDecorator.java,v 1.1 2012/08/17 14:51:02 marcin Exp $ + */ +public interface ThreadDecorator { + /** + * Notification that thread has just been created. + * @param thread the thread + */ + void threadCreated(Thread thread); + + /** + * Notification that thread has just been released from use + * (e.g. returned to a thread pool after executing a task). + * @param thread the thread + */ + void threadReleased(Thread thread); +} Index: 3rdParty_sources/jgroups/org/jgroups/util/ThreadFactory.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/ThreadFactory.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/ThreadFactory.java 17 Aug 2012 14:51:02 -0000 1.1 @@ -0,0 +1,11 @@ +package org.jgroups.util; + +public interface ThreadFactory extends java.util.concurrent.ThreadFactory{ + Thread newThread(Runnable r,String name); + Thread newThread(ThreadGroup group, Runnable r,String name); + void setPattern(String pattern); + void setIncludeClusterName(boolean includeClusterName); + void setClusterName(String channelName); + void setAddress(String address); + void renameThread(String base_name, Thread thread); +} Index: 3rdParty_sources/jgroups/org/jgroups/util/ThreadManager.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/ThreadManager.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/ThreadManager.java 17 Aug 2012 14:51:02 -0000 1.1 @@ -0,0 +1,22 @@ +package org.jgroups.util; + +/** + * An object that manages threads and provides callbacks to a + * {@link ThreadDecorator} to allow it to alter their state. + * + * @author Brian Stansberry + * @version $Id: ThreadManager.java,v 1.1 2012/08/17 14:51:02 marcin Exp $ + */ +public interface ThreadManager { + /** + * Gets the ThreadDecorator associated with this manager. + * @return the ThreadDecorator, or null if there is none. + */ + ThreadDecorator getThreadDecorator(); + + /** + * Sets the ThreadDecorator associated this manager should use. + * @param decorator the ThreadDecorator, or null. + */ + void setThreadDecorator(ThreadDecorator decorator); +} Index: 3rdParty_sources/jgroups/org/jgroups/util/ThreadManagerThreadPoolExecutor.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/ThreadManagerThreadPoolExecutor.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/ThreadManagerThreadPoolExecutor.java 17 Aug 2012 14:51:01 -0000 1.1 @@ -0,0 +1,58 @@ +package org.jgroups.util; + +import java.util.concurrent.*; +import java.util.concurrent.ThreadFactory; + +/** + * ThreadPoolExecutor subclass that implements @{link ThreadManager}. + * @author Brian Stansberry + * @version $Id: ThreadManagerThreadPoolExecutor.java,v 1.1 2012/08/17 14:51:01 marcin Exp $ + */ +public class ThreadManagerThreadPoolExecutor extends ThreadPoolExecutor implements ThreadManager { + private ThreadDecorator decorator; + + public ThreadManagerThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, + BlockingQueue workQueue) { + super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue); + } + + public ThreadManagerThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, + BlockingQueue workQueue, ThreadFactory threadFactory) { + super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory); + } + + public ThreadManagerThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, + BlockingQueue workQueue, RejectedExecutionHandler handler) { + super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler); + } + + public ThreadManagerThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, + BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { + super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler); + } + + public ThreadDecorator getThreadDecorator() { + return decorator; + } + + public void setThreadDecorator(ThreadDecorator decorator) { + this.decorator=decorator; + } + + /** + * Invokes {@link ThreadDecorator#threadReleased(Thread)} on the current thread. + *

        + * {@inheritDoc} + */ + @Override + protected void afterExecute(Runnable r, Throwable t) { + try { + super.afterExecute(r, t); + } + finally { + if(decorator != null) + decorator.threadReleased(Thread.currentThread()); + } + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/util/ThreadPool.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/ThreadPool.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/ThreadPool.java 17 Aug 2012 14:51:02 -0000 1.1 @@ -0,0 +1,101 @@ +// $Id: ThreadPool.java,v 1.1 2012/08/17 14:51:02 marcin Exp $ + +package org.jgroups.util; + + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + + +/** + * Maintains a set of ReusableThreads. When a thread is to be returned, all existing threads + * are checked: when one is available, it will be returned. Otherwise, a new thread is created + * and returned, unless the pool limit is reached, in which case null is returned. + * Creates threads only as needed, up to the MAX_NUM limit. However, does not shrink the pool + * when more threads become available than are used. + * @todo Shrink thread pool if threads are unused after some configurable time. + * @author Bela Ban + */ +public class ThreadPool { + int MAX_NUM=255; + int current_index=0; /// next available thread + ReusableThread[] pool=null; + boolean[] available_threads=null; + protected static final Log log=LogFactory.getLog(ThreadPool.class); + + + + public ThreadPool(int max_num) { + MAX_NUM=max_num; + pool=new ReusableThread[MAX_NUM]; + available_threads=new boolean[MAX_NUM]; + for(int i=0; i < pool.length; i++) { + pool[i]=null; + available_threads[i]=true; + } + if(log.isDebugEnabled()) log.debug("created a pool of " + MAX_NUM + " threads"); + } + + + public ReusableThread getThread() { + ReusableThread retval=null, tmp; + + synchronized(pool) { + // check whether a previously created thread can be reused + for(int i=0; i < current_index; i++) { + tmp=pool[i]; + if(tmp.available()) { + return tmp; + } + } + + // else create a new thread and add it to the pool + if(current_index >= MAX_NUM) { + if(log.isErrorEnabled()) log.error("could not create new thread because " + + "pool's max size reached (" + MAX_NUM + ") !"); + return null; + } + else { + retval=new ReusableThread(); + pool[current_index++]=retval; + return retval; + } + } + + } + + + public void destroy() { + deletePool(); + } + + + public String toString() { + StringBuilder ret=new StringBuilder(); + + synchronized(pool) { + ret.append("ThreadPool: capacity=" + pool.length + ", index=" + current_index + '\n'); + ret.append("Threads are:\n"); + for(int i=0; i < current_index; i++) + ret.append("[" + i + ": " + pool[i] + "]\n"); + } + return ret.toString(); + } + + + void deletePool() { + ReusableThread t; + synchronized(pool) { + for(int i=0; i < MAX_NUM; i++) { + t=pool[i]; + if(t != null) { + t.stop(); + pool[i]=null; + } + } + current_index=0; + } + } + + +} Index: 3rdParty_sources/jgroups/org/jgroups/util/TimeScheduler.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/TimeScheduler.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/TimeScheduler.java 17 Aug 2012 14:51:02 -0000 1.1 @@ -0,0 +1,285 @@ + +package org.jgroups.util; + + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jgroups.Global; + +import java.util.concurrent.*; + + +/** + * Fixed-delay & fixed-rate single thread scheduler + *

        + * The scheduler supports varying scheduling intervals by asking the task + * every time for its next preferred scheduling interval. Scheduling can + * either be fixed-delay or fixed-rate. The notions are + * borrowed from java.util.Timer and retain the same meaning. + * I.e. in fixed-delay scheduling, the task's new schedule is calculated + * as:
        + * new_schedule = time_task_starts + scheduling_interval + *

        + * In fixed-rate scheduling, the next schedule is calculated as:
        + * new_schedule = time_task_was_supposed_to_start + scheduling_interval + *

        + * The scheduler internally holds a queue of tasks sorted in ascending order + * according to their next execution time. A task is removed from the queue + * if it is cancelled, i.e. if TimeScheduler.Task.isCancelled() + * returns true. + *

        + * The scheduler internally uses a java.util.SortedSet to keep tasks + * sorted. java.util.Timer uses an array arranged as a binary heap + * that doesn't shrink. It is likely that the latter arrangement is faster. + *

        + * Initially, the scheduler is in SUSPENDed mode, start() + * need not be called: if a task is added, the scheduler gets started + * automatically. Calling start() starts the scheduler if it's + * suspended or stopped else has no effect. Once stop() is called, + * added tasks will not restart it: start() has to be called to + * restart the scheduler. + * @author Bela Ban + * @version $Id: TimeScheduler.java,v 1.1 2012/08/17 14:51:02 marcin Exp $ + */ +public class TimeScheduler extends ScheduledThreadPoolExecutor implements ThreadManager { + + /** The interface that submitted tasks must implement */ + public interface Task extends Runnable { + /** @return the next schedule interval. If <= 0 the task will not be re-scheduled */ + long nextInterval(); + } + + /** How many core threads */ + private static int TIMER_DEFAULT_NUM_THREADS=3; + + + protected static final Log log=LogFactory.getLog(TimeScheduler.class); + + + + static { + String tmp; + try { + tmp=System.getProperty(Global.TIMER_NUM_THREADS); + if(tmp != null) + TIMER_DEFAULT_NUM_THREADS=Integer.parseInt(tmp); + } + catch(Exception e) { + log.error("could not set number of timer threads", e); + } + } + + private ThreadDecorator threadDecorator=null; + + /** + * Create a scheduler that executes tasks in dynamically adjustable intervals + */ + public TimeScheduler() { + this(TIMER_DEFAULT_NUM_THREADS); + } + + public TimeScheduler(ThreadFactory factory) { + this(factory, TIMER_DEFAULT_NUM_THREADS); + } + + public TimeScheduler(ThreadFactory factory, int max_threads) { + super(max_threads, factory); + setRejectedExecutionHandler(new ShutdownRejectedExecutionHandler(getRejectedExecutionHandler())); + } + + public TimeScheduler(int corePoolSize) { + super(corePoolSize); + setRejectedExecutionHandler(new ShutdownRejectedExecutionHandler(getRejectedExecutionHandler())); + } + + public ThreadDecorator getThreadDecorator() { + return threadDecorator; + } + + public void setThreadDecorator(ThreadDecorator threadDecorator) { + this.threadDecorator=threadDecorator; + } + + public String dumpTaskQueue() { + return getQueue().toString(); + } + + + + /** + * Schedule a task for execution at varying intervals. After execution, the task will get rescheduled after + * {@link org.jgroups.util.TimeScheduler.Task#nextInterval()} milliseconds. The task is neve done until nextInterval() + * return a value <= 0 or the task is cancelled. + * @param task the task to execute + * @param relative scheduling scheme: true:
        + * Task is rescheduled relative to the last time it actually started execution

        + * false:
        Task is scheduled relative to its last execution schedule. This has the effect + * that the time between two consecutive executions of the task remains the same.

        + * Note that relative is always true; we always schedule the next execution relative to the last *actual* + * (not scheduled) execution + */ + public ScheduledFuture scheduleWithDynamicInterval(Task task, boolean relative) { + if(task == null) + throw new NullPointerException(); + + if (isShutdown()) + return null; + + TaskWrapper task_wrapper=new TaskWrapper(task); + task_wrapper.doSchedule(); // calls schedule() in ScheduledThreadPoolExecutor + return task_wrapper; + } + + + + + /** + * Add a task for execution at adjustable intervals + * @param t the task to execute + */ + public ScheduledFuture scheduleWithDynamicInterval(Task t) { + return scheduleWithDynamicInterval(t, true); + } + + /** + * Answers the number of tasks currently in the queue. + * @return The number of tasks currently in the queue. + */ + public int size() { + return getQueue().size(); + } + + + /** + * Start the scheduler, if it's suspended or stopped + */ + public void start() { + ; + } + + + /** + * Stop the scheduler if it's running. Switch to stopped, if it's + * suspended. Clear the task queue, cancelling all un-executed tasks + * + * @throws InterruptedException if interrupted while waiting for thread + * to return + */ + public void stop() throws InterruptedException { + java.util.List tasks=shutdownNow(); + for(Runnable task: tasks) { + if(task instanceof Future) { + Future future=(Future)task; + future.cancel(true); + } + } + getQueue().clear(); + awaitTermination(Global.THREADPOOL_SHUTDOWN_WAIT_TIME, TimeUnit.MILLISECONDS); + } + + + + + @Override + protected void afterExecute(Runnable r, Throwable t) + { + try { + super.afterExecute(r, t); + } + finally { + if(threadDecorator != null) + threadDecorator.threadReleased(Thread.currentThread()); + } + } + + + private class TaskWrapper implements Runnable, ScheduledFuture { + private final Task task; + private ScheduledFuture future; // cannot be null ! + private boolean cancelled=false; + + + public TaskWrapper(Task task) { + this.task=task; + } + + public ScheduledFuture getFuture() { + return future; + } + + public void run() { + try { + if(cancelled) { + if(future != null) + future.cancel(true); + return; + } + if(future != null && future.isCancelled()) + return; + task.run(); + } + catch(Throwable t) { + log.error("failed running task " + task, t); + } + + if(cancelled) { + if(future != null) + future.cancel(true); + return; + } + if(future != null && future.isCancelled()) + return; + + doSchedule(); + } + + + public void doSchedule() { + long next_interval=task.nextInterval(); + if(next_interval <= 0) { + if(log.isTraceEnabled()) + log.trace("task will not get rescheduled as interval is " + next_interval); + System.out.println("task will not get rescheduled as interval is " + next_interval); + } + else { + future=schedule(this, next_interval, TimeUnit.MILLISECONDS); + if(cancelled) + future.cancel(true); + } + } + + public int compareTo(Delayed o) { + long my_delay=future.getDelay(TimeUnit.MILLISECONDS), their_delay=o.getDelay(TimeUnit.MILLISECONDS); + return my_delay < their_delay? -1 : my_delay > their_delay? 1 : 0; + } + + public long getDelay(TimeUnit unit) { + return future != null? future.getDelay(unit) : -1; + } + + public boolean cancel(boolean mayInterruptIfRunning) { + cancelled=true; + if(future != null) + future.cancel(mayInterruptIfRunning); + return cancelled; + } + + public boolean isCancelled() { + return cancelled || (future != null && future.isCancelled()); + } + + public boolean isDone() { + return future == null || future.isDone(); + } + + public V get() throws InterruptedException, ExecutionException { + return null; + } + + public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + return null; + } + + } + +} Index: 3rdParty_sources/jgroups/org/jgroups/util/TimedWriter.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/TimedWriter.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/TimedWriter.java 17 Aug 2012 14:51:00 -0000 1.1 @@ -0,0 +1,277 @@ +// $Id: TimedWriter.java,v 1.1 2012/08/17 14:51:00 marcin Exp $ + +package org.jgroups.util; + + + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.Socket; + +/** + Waits until the buffer has been written to the output stream, or until timeout msecs have elapsed, + whichever comes first. + TODO: make it more generic, so all sorts of timed commands should be executable. Including return + values, exceptions and Timeout exception. Also use ReusableThread instead of creating a new threa + each time. + + @author Bela Ban +*/ + + +public class TimedWriter { + Thread thread=null; + long timeout=2000; + boolean completed=true; + Exception write_ex=null; + Socket sock=null; + static Log log=LogFactory.getLog(TimedWriter.class); + + + static class Timeout extends Exception { + public String toString() { + return "TimedWriter.Timeout"; + } + } + + + + class WriterThread extends Thread { + DataOutputStream out=null; + byte[] buf=null; + int i=0; + + + public WriterThread(OutputStream out, byte[] buf) { + super(Util.getGlobalThreadGroup(), "TimedWriter.WriterThread"); + this.out=new DataOutputStream(out); + this.buf=buf; + } + + public WriterThread(OutputStream out, int i) { + super(Util.getGlobalThreadGroup(), "TimedWriter.WriterThread"); + this.out=new DataOutputStream(out); + this.i=i; + } + + public void run() { + try { + if(buf != null) + out.write(buf); + else { + out.writeInt(i); + } + + } + catch(IOException e) { + write_ex=e; + } + completed=true; + } + } + + + class SocketCreator extends Thread { + InetAddress local=null, remote=null; + int peer_port=0; + + + public SocketCreator(InetAddress local, InetAddress remote, int peer_port) { + this.local=local; + this.remote=remote; + this.peer_port=peer_port; + } + + + public void run() { + completed=false; + sock=null; + + try { + sock=new Socket(remote, peer_port, local, 0); // 0 means choose any port + } + catch(IOException io_ex) { + write_ex=io_ex; + } + completed=true; + } + } + + + + + void start(InetAddress local, InetAddress remote, int peer_port) { + stop(); + thread=new SocketCreator(local, remote, peer_port); + thread.start(); + } + + + void start(OutputStream out, byte[] buf) { + stop(); + thread=new WriterThread(out, buf); + thread.start(); + } + + + void start(OutputStream out, int i) { + stop(); + thread=new WriterThread(out, i); + thread.start(); + } + + + + void stop() { + if(thread != null && thread.isAlive()) { + thread.interrupt(); + try {thread.join(timeout);} + catch(Exception e) {} + } + } + + + /** + Writes data to an output stream. If the method does not return within timeout milliseconds, + a Timeout exception will be thrown. + */ + public synchronized void write(OutputStream out, byte[] buf, long timeout) + throws Exception, Timeout, InterruptedException { + if(out == null || buf == null) { + log.error("TimedWriter.write(): output stream or buffer is null, ignoring write"); + return; + } + + try { + this.timeout=timeout; + completed=false; + start(out, buf); + if(thread == null) + return; + + thread.join(timeout); + + if(completed == false) { + throw new Timeout(); + } + if(write_ex != null) { + Exception tmp=write_ex; + write_ex=null; + throw tmp; + } + } + finally { // stop the thread in any case + stop(); + } + } + + + public synchronized void write(OutputStream out, int i, long timeout) + throws Exception, Timeout, InterruptedException { + if(out == null) { + log.error("TimedWriter.write(): output stream is null, ignoring write"); + return; + } + + try { + this.timeout=timeout; + completed=false; + start(out, i); + if(thread == null) + return; + + thread.join(timeout); + if(completed == false) { + throw new Timeout(); + } + if(write_ex != null) { + Exception tmp=write_ex; + write_ex=null; + throw tmp; + } + } + finally { // stop the thread in any case + stop(); + } + } + + + /** Tries to create a socket to remote_peer:remote_port. If not sucessful within timeout + milliseconds, throws the Timeout exception. Otherwise, returns the socket or throws an + IOException. */ + public synchronized Socket createSocket(InetAddress local, InetAddress remote, int port, long timeout) + throws Exception, Timeout, InterruptedException { + + try { + this.timeout=timeout; + completed=false; + start(local, remote, port); + if(thread == null) + return null; + + thread.join(timeout); + if(completed == false) { + throw new Timeout(); + } + if(write_ex != null) { + Exception tmp=write_ex; + write_ex=null; + throw tmp; + } + return sock; + } + finally { // stop the thread in any case + stop(); + } + } + + + + + public static void main(String[] args) { + TimedWriter w=new TimedWriter(); + InetAddress local=null; + InetAddress remote=null; + int port=0; + Socket sock=null ; + + if(args.length != 3) { + log.error("TimedWriter "); + return; + } + + try { + local=InetAddress.getByName(args[0]); + remote=InetAddress.getByName(args[1]); + port=Integer.parseInt(args[2]); + } + catch(Exception e) { + log.error("Could find host " + remote); + return; + } + + while(true) { + + try { + sock=w.createSocket(local, remote, port, 3000); + if(sock != null) { + System.out.println("Connection created"); + return; + } + } + catch(TimedWriter.Timeout t) { + log.error("Timed out creating socket"); + } + catch(Exception io_ex) { + log.error("Connection could not be created, retrying"); + Util.sleep(2000); + } + } + + } +} Index: 3rdParty_sources/jgroups/org/jgroups/util/Triple.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/Triple.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/Triple.java 17 Aug 2012 14:51:00 -0000 1.1 @@ -0,0 +1,47 @@ +package org.jgroups.util; + +/** + * Holds 3 values, useful when we have a map with a key, but more than 1 value and we don't want to create a separate + * holder object for the values, and don't want to pass the values as a list or array. + * @author Bela Ban + * @version $Id: Triple.java,v 1.1 2012/08/17 14:51:00 marcin Exp $ + */ +public class Triple { + private V1 val1; + private V2 val2; + private V3 val3; + + public Triple(V1 val1, V2 val2, V3 val3) { + this.val1=val1; + this.val2=val2; + this.val3=val3; + } + + public V1 getVal1() { + return val1; + } + + public void setVal1(V1 val1) { + this.val1=val1; + } + + public V2 getVal2() { + return val2; + } + + public void setVal2(V2 val2) { + this.val2=val2; + } + + public V3 getVal3() { + return val3; + } + + public void setVal3(V3 val3) { + this.val3=val3; + } + + public String toString() { + return val1 + " : " + val2 + " : " + val3; + } +} \ No newline at end of file Index: 3rdParty_sources/jgroups/org/jgroups/util/Tuple.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/Tuple.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/Tuple.java 17 Aug 2012 14:51:01 -0000 1.1 @@ -0,0 +1,37 @@ +package org.jgroups.util; + +/** + * Holds 2 values, useful when we have a map with a key, but more than 1 value and we don't want to create a separate + * holder object for the values, and don't want to pass the values as a list or array. + * @author Bela Ban + * @version $Id: Tuple.java,v 1.1 2012/08/17 14:51:01 marcin Exp $ + */ +public class Tuple { + private V1 val1; + private V2 val2; + + public Tuple(V1 val1, V2 val2) { + this.val1=val1; + this.val2=val2; + } + + public V1 getVal1() { + return val1; + } + + public void setVal1(V1 val1) { + this.val1=val1; + } + + public V2 getVal2() { + return val2; + } + + public void setVal2(V2 val2) { + this.val2=val2; + } + + public String toString() { + return val1 + " : " + val2; + } +} Index: 3rdParty_sources/jgroups/org/jgroups/util/UnmodifiableVector.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/UnmodifiableVector.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/UnmodifiableVector.java 17 Aug 2012 14:51:01 -0000 1.1 @@ -0,0 +1,267 @@ +package org.jgroups.util; + +import java.util.*; + +/** + * Vector which cannot be modified + * @author Bela Ban + * @version $Id: UnmodifiableVector.java,v 1.1 2012/08/17 14:51:01 marcin Exp $ + */ +public class UnmodifiableVector extends Vector { + Vector v; + + public UnmodifiableVector(Vector v) { + this.v=v; + } + + public synchronized void copyInto(Object[] anArray) { + v.copyInto(anArray); + } + + public synchronized void trimToSize() { + throw new UnsupportedOperationException(); + } + + public synchronized void ensureCapacity(int minCapacity) { + throw new UnsupportedOperationException(); + } + + public synchronized void setSize(int newSize) { + throw new UnsupportedOperationException(); + } + + public synchronized int capacity() { + return v.capacity(); + } + + public synchronized int size() { + return v.size(); + } + + public synchronized boolean isEmpty() { + return v.isEmpty(); + } + + public Enumeration elements() { + return v.elements(); + } + + public boolean contains(Object elem) { + return v.contains(elem); + } + + public int indexOf(Object elem) { + return v.indexOf(elem); + } + + public synchronized int indexOf(Object elem, int index) { + return v.indexOf(elem, index); + } + + public synchronized int lastIndexOf(Object elem) { + return v.lastIndexOf(elem); + } + + public synchronized int lastIndexOf(Object elem, int index) { + return v.lastIndexOf(elem, index); + } + + public synchronized Object elementAt(int index) { + return v.elementAt(index); + } + + public synchronized Object firstElement() { + return v.firstElement(); + } + + public synchronized Object lastElement() { + return v.lastElement(); + } + + public synchronized void setElementAt(Object obj, int index) { + v.setElementAt(obj, index); + } + + public synchronized void removeElementAt(int index) { + throw new UnsupportedOperationException(); + } + + public synchronized void insertElementAt(Object obj, int index) { + throw new UnsupportedOperationException(); + } + + public synchronized void addElement(Object obj) { + throw new UnsupportedOperationException(); + } + + public synchronized boolean removeElement(Object obj) { + throw new UnsupportedOperationException(); + } + + public synchronized void removeAllElements() { + throw new UnsupportedOperationException(); + } + + public synchronized Object clone() { + return v.clone(); + } + + public synchronized Object[] toArray() { + return v.toArray(); + } + + public synchronized Object[] toArray(Object[] a) { + return v.toArray(a); + } + + public synchronized Object get(int index) { + return v.get(index); + } + + public synchronized Object set(int index, Object element) { + throw new UnsupportedOperationException(); + } + + public synchronized boolean add(Object o) { + throw new UnsupportedOperationException(); + } + + public boolean remove(Object o) { + throw new UnsupportedOperationException(); + } + + public void add(int index, Object element) { + throw new UnsupportedOperationException(); + } + + public synchronized Object remove(int index) { + throw new UnsupportedOperationException(); + } + + public void clear() { + throw new UnsupportedOperationException(); + } + + public synchronized boolean containsAll(Collection c) { + return v.containsAll(c); + } + + public synchronized boolean addAll(Collection c) { + throw new UnsupportedOperationException(); + } + + public synchronized boolean removeAll(Collection c) { + throw new UnsupportedOperationException(); + } + + public synchronized boolean retainAll(Collection c) { + throw new UnsupportedOperationException(); + } + + public synchronized boolean addAll(int index, Collection c) { + throw new UnsupportedOperationException(); + } + + public synchronized boolean equals(Object o) { + return v.equals(o); + } + + public synchronized int hashCode() { + return v.hashCode(); + } + + public synchronized String toString() { + return v.toString(); + } + + public synchronized java.util.List subList(int fromIndex, int toIndex) { + return v.subList(fromIndex, toIndex); + } + + public ListIterator listIterator() { + return new ListIterator() { + ListIterator i = v.listIterator(); + + public boolean hasNext() {return i.hasNext();} + public Object next() {return i.next();} + + public boolean hasPrevious() { + return i.hasPrevious(); + } + + public Object previous() { + return i.previous(); + } + + public int nextIndex() { + return i.nextIndex(); + } + + public int previousIndex() { + return i.previousIndex(); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + + public void set(Object o) { + throw new UnsupportedOperationException(); + } + + public void add(Object o) { + throw new UnsupportedOperationException(); + } + }; + } + + public ListIterator listIterator(final int index) { + return new ListIterator() { + ListIterator i = v.listIterator(index); + + public boolean hasNext() {return i.hasNext();} + public Object next() {return i.next();} + + public boolean hasPrevious() { + return i.hasPrevious(); + } + + public Object previous() { + return i.previous(); + } + + public int nextIndex() { + return i.nextIndex(); + } + + public int previousIndex() { + return i.previousIndex(); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + + public void set(Object o) { + throw new UnsupportedOperationException(); + } + + public void add(Object o) { + throw new UnsupportedOperationException(); + } + }; + } + + + public Iterator iterator() { + return new Iterator() { + Iterator i = v.iterator(); + + public boolean hasNext() {return i.hasNext();} + public Object next() {return i.next();} + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } +} Index: 3rdParty_sources/jgroups/org/jgroups/util/Util.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/Util.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/Util.java 17 Aug 2012 14:51:01 -0000 1.1 @@ -0,0 +1,2650 @@ +package org.jgroups.util; + +import org.apache.commons.logging.LogFactory; +import org.apache.commons.logging.Log; +import org.jgroups.*; +import org.jgroups.auth.AuthToken; +import org.jgroups.conf.ClassConfigurator; +import org.jgroups.protocols.FD; +import org.jgroups.protocols.PingHeader; +import org.jgroups.protocols.UdpHeader; +import org.jgroups.protocols.PingRsp; +import org.jgroups.protocols.pbcast.NakAckHeader; +import org.jgroups.stack.IpAddress; +import org.jgroups.stack.Protocol; + +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import java.io.*; +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; +import java.net.*; +import java.nio.ByteBuffer; +import java.nio.channels.WritableByteChannel; +import java.security.MessageDigest; +import java.text.NumberFormat; +import java.util.*; + + +/** + * Collection of various utility routines that can not be assigned to other classes. + * @author Bela Ban + * @version $Id: Util.java,v 1.1 2012/08/17 14:51:01 marcin Exp $ + */ +public class Util { + + private static NumberFormat f; + + private static Map PRIMITIVE_TYPES=new HashMap(10); + private static final byte TYPE_NULL = 0; + private static final byte TYPE_STREAMABLE = 1; + private static final byte TYPE_SERIALIZABLE = 2; + + private static final byte TYPE_BOOLEAN = 10; + private static final byte TYPE_BYTE = 11; + private static final byte TYPE_CHAR = 12; + private static final byte TYPE_DOUBLE = 13; + private static final byte TYPE_FLOAT = 14; + private static final byte TYPE_INT = 15; + private static final byte TYPE_LONG = 16; + private static final byte TYPE_SHORT = 17; + private static final byte TYPE_STRING = 18; + + // constants + public static final int MAX_PORT=65535; // highest port allocatable + static boolean resolve_dns=false; + + static boolean JGROUPS_COMPAT=false; + + /** + * Global thread group to which all (most!) JGroups threads belong + */ + private static ThreadGroup GLOBAL_GROUP=new ThreadGroup("JGroups") { + public void uncaughtException(Thread t, Throwable e) { + LogFactory.getLog("org.jgroups").error("uncaught exception in " + t + " (thread group=" + GLOBAL_GROUP + " )", e); + } + }; + + public static ThreadGroup getGlobalThreadGroup() { + return GLOBAL_GROUP; + } + + + static { + /* Trying to get value of resolve_dns. PropertyPermission not granted if + * running in an untrusted environment with JNLP */ + try { + resolve_dns=Boolean.valueOf(System.getProperty("resolve.dns", "false")).booleanValue(); + } + catch (SecurityException ex){ + resolve_dns=false; + } + f=NumberFormat.getNumberInstance(); + f.setGroupingUsed(false); + f.setMaximumFractionDigits(2); + + try { + String tmp=Util.getProperty(new String[]{Global.MARSHALLING_COMPAT}, null, null, false, "false"); + JGROUPS_COMPAT=Boolean.valueOf(tmp).booleanValue(); + } + catch (SecurityException ex){ + } + + PRIMITIVE_TYPES.put(Boolean.class, new Byte(TYPE_BOOLEAN)); + PRIMITIVE_TYPES.put(Byte.class, new Byte(TYPE_BYTE)); + PRIMITIVE_TYPES.put(Character.class, new Byte(TYPE_CHAR)); + PRIMITIVE_TYPES.put(Double.class, new Byte(TYPE_DOUBLE)); + PRIMITIVE_TYPES.put(Float.class, new Byte(TYPE_FLOAT)); + PRIMITIVE_TYPES.put(Integer.class, new Byte(TYPE_INT)); + PRIMITIVE_TYPES.put(Long.class, new Byte(TYPE_LONG)); + PRIMITIVE_TYPES.put(Short.class, new Byte(TYPE_SHORT)); + PRIMITIVE_TYPES.put(String.class, new Byte(TYPE_STRING)); + } + + + /** + * Verifies that val is <= max memory + * @param buf_name + * @param val + */ + public static void checkBufferSize(String buf_name, long val) { + // sanity check that max_credits doesn't exceed memory allocated to VM by -Xmx + long max_mem=Runtime.getRuntime().maxMemory(); + if(val > max_mem) { + throw new IllegalArgumentException(buf_name + "(" + Util.printBytes(val) + ") exceeds max memory allocated to VM (" + + Util.printBytes(max_mem) + ")"); + } + } + + + public static int keyPress(String msg) { + System.out.println(msg); + + try { + return System.in.read(); + } + catch(IOException e) { + return 0; + } + } + + + public static void close(InputStream inp) { + if(inp != null) + try {inp.close();} catch(IOException e) {} + } + + public static void close(OutputStream out) { + if(out != null) { + try {out.close();} catch(IOException e) {} + } + } + + public static void close(Socket s) { + if(s != null) { + try {s.close();} catch(Exception ex) {} + } + } + + public static void close(DatagramSocket my_sock) { + if(my_sock != null) { + try {my_sock.close();} catch(Throwable t) {} + } + } + + public static void close(Channel ch) { + if(ch != null) { + try {ch.close();} catch(Throwable t) {} + } + } + + public static void close(Channel ... channels) { + if(channels != null) { + for(Channel ch: channels) + Util.close(ch); + } + } + + + /** + * Creates an object from a byte buffer + */ + public static Object objectFromByteBuffer(byte[] buffer) throws Exception { + if(buffer == null) return null; + if(JGROUPS_COMPAT) + return oldObjectFromByteBuffer(buffer); + return objectFromByteBuffer(buffer, 0, buffer.length); + } + + + public static Object objectFromByteBuffer(byte[] buffer, int offset, int length) throws Exception { + if(buffer == null) return null; + if(JGROUPS_COMPAT) + return oldObjectFromByteBuffer(buffer, offset, length); + Object retval=null; + InputStream in=null; + ByteArrayInputStream in_stream=new ByteArrayInputStream(buffer, offset, length); + byte b=(byte)in_stream.read(); + + try { + switch(b) { + case TYPE_NULL: + return null; + case TYPE_STREAMABLE: + in=new DataInputStream(in_stream); + retval=readGenericStreamable((DataInputStream)in); + break; + case TYPE_SERIALIZABLE: // the object is Externalizable or Serializable + in=new ContextObjectInputStream(in_stream); // changed Nov 29 2004 (bela) + retval=((ContextObjectInputStream)in).readObject(); + break; + case TYPE_BOOLEAN: + in=new DataInputStream(in_stream); + retval=Boolean.valueOf(((DataInputStream)in).readBoolean()); + break; + case TYPE_BYTE: + in=new DataInputStream(in_stream); + retval=new Byte(((DataInputStream)in).readByte()); + break; + case TYPE_CHAR: + in=new DataInputStream(in_stream); + retval=new Character(((DataInputStream)in).readChar()); + break; + case TYPE_DOUBLE: + in=new DataInputStream(in_stream); + retval=new Double(((DataInputStream)in).readDouble()); + break; + case TYPE_FLOAT: + in=new DataInputStream(in_stream); + retval=new Float(((DataInputStream)in).readFloat()); + break; + case TYPE_INT: + in=new DataInputStream(in_stream); + retval=new Integer(((DataInputStream)in).readInt()); + break; + case TYPE_LONG: + in=new DataInputStream(in_stream); + retval=new Long(((DataInputStream)in).readLong()); + break; + case TYPE_SHORT: + in=new DataInputStream(in_stream); + retval=new Short(((DataInputStream)in).readShort()); + break; + case TYPE_STRING: + in=new DataInputStream(in_stream); + if(((DataInputStream)in).readBoolean()) { // large string + ObjectInputStream ois=new ObjectInputStream(in); + try { + return ois.readObject(); + } + finally { + ois.close(); + } + } + else { + retval=((DataInputStream)in).readUTF(); + } + break; + default: + throw new IllegalArgumentException("type " + b + " is invalid"); + } + return retval; + } + finally { + Util.close(in); + } + } + + + + + /** + * Serializes/Streams an object into a byte buffer. + * The object has to implement interface Serializable or Externalizable + * or Streamable. Only Streamable objects are interoperable w/ jgroups-me + */ + public static byte[] objectToByteBuffer(Object obj) throws Exception { + + if(JGROUPS_COMPAT) + return oldObjectToByteBuffer(obj); + + byte[] result=null; + final ByteArrayOutputStream out_stream=new ByteArrayOutputStream(512); + + if(obj == null) { + out_stream.write(TYPE_NULL); + out_stream.flush(); + return out_stream.toByteArray(); + } + + OutputStream out=null; + Byte type; + try { + if(obj instanceof Streamable) { // use Streamable if we can + out_stream.write(TYPE_STREAMABLE); + out=new DataOutputStream(out_stream); + writeGenericStreamable((Streamable)obj, (DataOutputStream)out); + } + else if((type=(Byte)PRIMITIVE_TYPES.get(obj.getClass())) != null) { + out_stream.write(type.byteValue()); + out=new DataOutputStream(out_stream); + switch(type.byteValue()) { + case TYPE_BOOLEAN: + ((DataOutputStream)out).writeBoolean(((Boolean)obj).booleanValue()); + break; + case TYPE_BYTE: + ((DataOutputStream)out).writeByte(((Byte)obj).byteValue()); + break; + case TYPE_CHAR: + ((DataOutputStream)out).writeChar(((Character)obj).charValue()); + break; + case TYPE_DOUBLE: + ((DataOutputStream)out).writeDouble(((Double)obj).doubleValue()); + break; + case TYPE_FLOAT: + ((DataOutputStream)out).writeFloat(((Float)obj).floatValue()); + break; + case TYPE_INT: + ((DataOutputStream)out).writeInt(((Integer)obj).intValue()); + break; + case TYPE_LONG: + ((DataOutputStream)out).writeLong(((Long)obj).longValue()); + break; + case TYPE_SHORT: + ((DataOutputStream)out).writeShort(((Short)obj).shortValue()); + break; + case TYPE_STRING: + String str=(String)obj; + if(str.length() > Short.MAX_VALUE) { + ((DataOutputStream)out).writeBoolean(true); + ObjectOutputStream oos=new ObjectOutputStream(out); + try { + oos.writeObject(str); + } + finally { + oos.close(); + } + } + else { + ((DataOutputStream)out).writeBoolean(false); + ((DataOutputStream)out).writeUTF(str); + } + break; + default: + throw new IllegalArgumentException("type " + type + " is invalid"); + } + } + else { // will throw an exception if object is not serializable + out_stream.write(TYPE_SERIALIZABLE); + out=new ObjectOutputStream(out_stream); + ((ObjectOutputStream)out).writeObject(obj); + } + } + finally { + Util.close(out); + } + result=out_stream.toByteArray(); + return result; + } + + + + + /** For backward compatibility in JBoss 4.0.2 */ + public static Object oldObjectFromByteBuffer(byte[] buffer) throws Exception { + if(buffer == null) return null; + return oldObjectFromByteBuffer(buffer, 0, buffer.length); + } + + public static Object oldObjectFromByteBuffer(byte[] buffer, int offset, int length) throws Exception { + if(buffer == null) return null; + Object retval=null; + + try { // to read the object as an Externalizable + ByteArrayInputStream in_stream=new ByteArrayInputStream(buffer, offset, length); + ObjectInputStream in=new ContextObjectInputStream(in_stream); // changed Nov 29 2004 (bela) + retval=in.readObject(); + in.close(); + } + catch(StreamCorruptedException sce) { + try { // is it Streamable? + ByteArrayInputStream in_stream=new ByteArrayInputStream(buffer, offset, length); + DataInputStream in=new DataInputStream(in_stream); + retval=readGenericStreamable(in); + in.close(); + } + catch(Exception ee) { + IOException tmp=new IOException("unmarshalling failed"); + tmp.initCause(ee); + throw tmp; + } + } + + if(retval == null) + return null; + return retval; + } + + + + + /** + * Serializes/Streams an object into a byte buffer. + * The object has to implement interface Serializable or Externalizable + * or Streamable. Only Streamable objects are interoperable w/ jgroups-me + */ + public static byte[] oldObjectToByteBuffer(Object obj) throws Exception { + byte[] result=null; + final ByteArrayOutputStream out_stream=new ByteArrayOutputStream(512); + if(obj instanceof Streamable) { // use Streamable if we can + DataOutputStream out=new DataOutputStream(out_stream); + writeGenericStreamable((Streamable)obj, out); + out.close(); + } + else { + ObjectOutputStream out=new ObjectOutputStream(out_stream); + out.writeObject(obj); + out.close(); + } + result=out_stream.toByteArray(); + return result; + } + + + + public static Streamable streamableFromByteBuffer(Class cl, byte[] buffer) throws Exception { + if(buffer == null) return null; + Streamable retval=null; + ByteArrayInputStream in_stream=new ByteArrayInputStream(buffer); + DataInputStream in=new DataInputStream(in_stream); // changed Nov 29 2004 (bela) + retval=(Streamable)cl.newInstance(); + retval.readFrom(in); + in.close(); + return retval; + } + + + public static Streamable streamableFromByteBuffer(Class cl, byte[] buffer, int offset, int length) throws Exception { + if(buffer == null) return null; + Streamable retval=null; + ByteArrayInputStream in_stream=new ByteArrayInputStream(buffer, offset, length); + DataInputStream in=new DataInputStream(in_stream); // changed Nov 29 2004 (bela) + retval=(Streamable)cl.newInstance(); + retval.readFrom(in); + in.close(); + return retval; + } + + public static byte[] streamableToByteBuffer(Streamable obj) throws Exception { + byte[] result=null; + final ByteArrayOutputStream out_stream=new ByteArrayOutputStream(512); + DataOutputStream out=new DataOutputStream(out_stream); + obj.writeTo(out); + result=out_stream.toByteArray(); + out.close(); + return result; + } + + + public static byte[] collectionToByteBuffer(Collection c) throws Exception { + byte[] result=null; + final ByteArrayOutputStream out_stream=new ByteArrayOutputStream(512); + DataOutputStream out=new DataOutputStream(out_stream); + Util.writeAddresses(c, out); + result=out_stream.toByteArray(); + out.close(); + return result; + } + + public static int size(Address addr) { + int retval=Global.BYTE_SIZE; // presence byte + if(addr != null) + retval+=addr.size() + Global.BYTE_SIZE; // plus type of address + return retval; + } + + public static void writeAuthToken(AuthToken token, DataOutputStream out) throws IOException{ + Util.writeString(token.getName(), out); + token.writeTo(out); + } + + public static AuthToken readAuthToken(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + try{ + String type = Util.readString(in); + Object obj = Class.forName(type).newInstance(); + AuthToken token = (AuthToken) obj; + token.readFrom(in); + return token; + } + catch(ClassNotFoundException cnfe){ + return null; + } + } + + public static void writeAddress(Address addr, DataOutputStream out) throws IOException { + if(addr == null) { + out.writeBoolean(false); + return; + } + + out.writeBoolean(true); + if(addr instanceof IpAddress) { + // regular case, we don't need to include class information about the type of Address, e.g. JmsAddress + out.writeBoolean(true); + addr.writeTo(out); + } + else { + out.writeBoolean(false); + writeOtherAddress(addr, out); + } + } + + public static Address readAddress(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + Address addr=null; + if(in.readBoolean() == false) + return null; + if(in.readBoolean()) { + addr=new IpAddress(); + addr.readFrom(in); + } + else { + addr=readOtherAddress(in); + } + return addr; + } + + private static Address readOtherAddress(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + ClassConfigurator conf; + try { + conf=ClassConfigurator.getInstance(false); + } + catch(ChannelException e) { + IllegalAccessException new_ex=new IllegalAccessException(); + new_ex.initCause(e); + throw new_ex; + } + int b=in.read(); + short magic_number; + String classname; + Class cl=null; + Address addr; + if(b == 1) { + magic_number=in.readShort(); + cl=conf.get(magic_number); + } + else { + classname=in.readUTF(); + cl=conf.get(classname); + } + addr=(Address)cl.newInstance(); + addr.readFrom(in); + return addr; + } + + private static void writeOtherAddress(Address addr, DataOutputStream out) throws IOException { + ClassConfigurator conf=null; + try { + conf=ClassConfigurator.getInstance(false); + } + catch(ChannelException e) { + IOException new_ex=new IOException(); + new_ex.initCause(e); + throw new_ex; + } + short magic_number=conf != null? conf.getMagicNumber(addr.getClass()) : -1; + + // write the class info + if(magic_number == -1) { + out.write(0); + out.writeUTF(addr.getClass().getName()); + } + else { + out.write(1); + out.writeShort(magic_number); + } + + // write the data itself + addr.writeTo(out); + } + + /** + * Writes a Vector of Addresses. Can contain 65K addresses at most + * @param v A Collection

        + * @param out + * @throws IOException + */ + public static void writeAddresses(Collection v, DataOutputStream out) throws IOException { + if(v == null) { + out.writeShort(-1); + return; + } + out.writeShort(v.size()); + Address addr; + for(Iterator it=v.iterator(); it.hasNext();) { + addr=(Address)it.next(); + Util.writeAddress(addr, out); + } + } + + /** + * + * @param in + * @param cl The type of Collection, e.g. Vector.class + * @return Collection of Address objects + * @throws IOException + * @throws IllegalAccessException + * @throws InstantiationException + */ + public static Collection
        readAddresses(DataInputStream in, Class cl) throws IOException, IllegalAccessException, InstantiationException { + short length=in.readShort(); + if(length < 0) return null; + Collection
        retval=(Collection
        )cl.newInstance(); + Address addr; + for(int i=0; i < length; i++) { + addr=Util.readAddress(in); + retval.add(addr); + } + return retval; + } + + + /** + * Returns the marshalled size of a Collection of Addresses. + * Assumes elements are of the same type ! + * @param addrs Collection
        + * @return long size + */ + public static long size(Collection addrs) { + int retval=Global.SHORT_SIZE; // number of elements + if(addrs != null && !addrs.isEmpty()) { + Address addr=(Address)addrs.iterator().next(); + retval+=size(addr) * addrs.size(); + } + return retval; + } + + + + + public static void writeStreamable(Streamable obj, DataOutputStream out) throws IOException { + if(obj == null) { + out.writeBoolean(false); + return; + } + out.writeBoolean(true); + obj.writeTo(out); + } + + + public static Streamable readStreamable(Class clazz, DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { + Streamable retval=null; + if(in.readBoolean() == false) + return null; + retval=(Streamable)clazz.newInstance(); + retval.readFrom(in); + return retval; + } + + + public static void writeGenericStreamable(Streamable obj, DataOutputStream out) throws IOException { + short magic_number; + String classname; + + if(obj == null) { + out.write(0); + return; + } + + try { + out.write(1); + magic_number=ClassConfigurator.getInstance(false).getMagicNumber(obj.getClass()); + // write the magic number or the class name + if(magic_number == -1) { + out.writeBoolean(false); + classname=obj.getClass().getName(); + out.writeUTF(classname); + } + else { + out.writeBoolean(true); + out.writeShort(magic_number); + } + + // write the contents + obj.writeTo(out); + } + catch(ChannelException e) { + throw new IOException("failed writing object of type " + obj.getClass() + " to stream: " + e.toString()); + } + } + + + + public static Streamable readGenericStreamable(DataInputStream in) throws IOException { + Streamable retval=null; + int b=in.read(); + if(b == 0) + return null; + + boolean use_magic_number=in.readBoolean(); + String classname; + Class clazz; + + try { + if(use_magic_number) { + short magic_number=in.readShort(); + clazz=ClassConfigurator.getInstance(false).get(magic_number); + if (clazz==null) { + throw new ClassNotFoundException("Class for magic number "+magic_number+" cannot be found."); + } + } + else { + classname=in.readUTF(); + clazz=ClassConfigurator.getInstance(false).get(classname); + if (clazz==null) { + throw new ClassNotFoundException(classname); + } + } + + retval=(Streamable)clazz.newInstance(); + retval.readFrom(in); + return retval; + } + catch(Exception ex) { + throw new IOException("failed reading object: " + ex.toString()); + } + } + + public static void writeObject(Object obj, DataOutputStream out) throws Exception { + if(obj == null || !(obj instanceof Streamable)) { + byte[] buf=objectToByteBuffer(obj); + out.writeShort(buf.length); + out.write(buf, 0, buf.length); + } + else { + out.writeShort(-1); + writeGenericStreamable((Streamable)obj, out); + } + } + + public static Object readObject(DataInputStream in) throws Exception { + short len=in.readShort(); + Object retval=null; + if(len == -1) { + retval=readGenericStreamable(in); + } + else { + byte[] buf=new byte[len]; + in.readFully(buf, 0, len); + retval=objectFromByteBuffer(buf); + } + return retval; + } + + + + public static void writeString(String s, DataOutputStream out) throws IOException { + if(s != null) { + out.write(1); + out.writeUTF(s); + } + else { + out.write(0); + } + } + + public static String readString(DataInputStream in) throws IOException { + int b=in.read(); + if(b == 1) + return in.readUTF(); + return null; + } + + public static void writeByteBuffer(byte[] buf, DataOutputStream out) throws IOException { + if(buf != null) { + out.write(1); + out.writeInt(buf.length); + out.write(buf, 0, buf.length); + } + else { + out.write(0); + } + } + + public static byte[] readByteBuffer(DataInputStream in) throws IOException { + int b=in.read(); + if(b == 1) { + b=in.readInt(); + byte[] buf=new byte[b]; + in.read(buf, 0, buf.length); + return buf; + } + return null; + } + + + public static Buffer messageToByteBuffer(Message msg) throws IOException { + ExposedByteArrayOutputStream output=new ExposedByteArrayOutputStream(512); + DataOutputStream out=new DataOutputStream(output); + + out.writeBoolean(msg != null); + if(msg != null) + msg.writeTo(out); + out.flush(); + Buffer retval=new Buffer(output.getRawBuffer(), 0, output.size()); + out.close(); + output.close(); + return retval; + } + + public static Message byteBufferToMessage(byte[] buffer, int offset, int length) throws Exception { + ByteArrayInputStream input=new ByteArrayInputStream(buffer, offset, length); + DataInputStream in=new DataInputStream(input); + + if(!in.readBoolean()) + return null; + + Message msg=new Message(false); // don't create headers, readFrom() will do this + msg.readFrom(in); + return msg; + } + + + + /** + * Marshalls a list of messages. + * @param xmit_list LinkedList + * @return Buffer + * @throws IOException + */ + public static Buffer msgListToByteBuffer(List xmit_list) throws IOException { + ExposedByteArrayOutputStream output=new ExposedByteArrayOutputStream(512); + DataOutputStream out=new DataOutputStream(output); + Buffer retval=null; + + out.writeInt(xmit_list.size()); + for(Message msg: xmit_list) { + msg.writeTo(out); + } + out.flush(); + retval=new Buffer(output.getRawBuffer(), 0, output.size()); + out.close(); + output.close(); + return retval; + } + + public static List byteBufferToMessageList(byte[] buffer, int offset, int length) throws Exception { + List retval=null; + ByteArrayInputStream input=new ByteArrayInputStream(buffer, offset, length); + DataInputStream in=new DataInputStream(input); + int size=in.readInt(); + + if(size == 0) + return null; + + Message msg; + retval=new LinkedList(); + for(int i=0; i < size; i++) { + msg=new Message(false); // don't create headers, readFrom() will do this + msg.readFrom(in); + retval.add(msg); + } + + return retval; + } + + + + + + public static boolean match(Object obj1, Object obj2) { + if(obj1 == null && obj2 == null) + return true; + if(obj1 != null) + return obj1.equals(obj2); + else + return obj2.equals(obj1); + } + + + public static boolean match(long[] a1, long[] a2) { + if(a1 == null && a2 == null) + return true; + if(a1 == null || a2 == null) + return false; + + if(a1 == a2) // identity + return true; + + // at this point, a1 != null and a2 != null + if(a1.length != a2.length) + return false; + + for(int i=0; i < a1.length; i++) { + if(a1[i] != a2[i]) + return false; + } + return true; + } + + /** Sleep for timeout msecs. Returns when timeout has elapsed or thread was interrupted */ + public static void sleep(long timeout) { + try { + Thread.sleep(timeout); + } + catch(Throwable e) { + } + } + + + public static void sleep(long timeout, int nanos) { + try { + Thread.sleep(timeout, nanos); + } + catch(Throwable e) { + } + } + + + /** + * On most UNIX systems, the minimum sleep time is 10-20ms. Even if we specify sleep(1), the thread will + * sleep for at least 10-20ms. On Windows, sleep() seems to be implemented as a busy sleep, that is the + * thread never relinquishes control and therefore the sleep(x) is exactly x ms long. + */ + public static void sleep(long msecs, boolean busy_sleep) { + if(!busy_sleep) { + sleep(msecs); + return; + } + + long start=System.currentTimeMillis(); + long stop=start + msecs; + + while(stop > start) { + start=System.currentTimeMillis(); + } + } + + + /** Returns a random value in the range [1 - range] */ + public static long random(long range) { + return (long)((Math.random() * range) % range) + 1; + } + + + /** Sleeps between 1 and timeout milliseconds, chosen randomly. Timeout must be > 1 */ + public static void sleepRandom(long timeout) { + if(timeout <= 0) { + return; + } + + long r=(int)((Math.random() * 100000) % timeout) + 1; + sleep(r); + } + + /** Sleeps between floor and ceiling milliseconds, chosen randomly */ + public static void sleepRandom(long floor, long ceiling) { + if(ceiling - floor<= 0) { + return; + } + long diff = ceiling - floor; + long r=(int)((Math.random() * 100000) % diff) + floor; + sleep(r); + } + + + /** + Tosses a coin weighted with probability and returns true or false. Example: if probability=0.8, + chances are that in 80% of all cases, true will be returned and false in 20%. + */ + public static boolean tossWeightedCoin(double probability) { + long r=random(100); + long cutoff=(long)(probability * 100); + return r < cutoff; + } + + + public static String getHostname() { + try { + return InetAddress.getLocalHost().getHostName(); + } + catch(Exception ex) { + } + return "localhost"; + } + + + public static void dumpStack(boolean exit) { + try { + throw new Exception("Dumping stack:"); + } + catch(Exception e) { + e.printStackTrace(); + if(exit) + System.exit(0); + } + } + + + public static String dumpThreads() { + StringBuilder sb=new StringBuilder(); + ThreadMXBean bean=ManagementFactory.getThreadMXBean(); + long[] ids=bean.getAllThreadIds(); + ThreadInfo[] threads=bean.getThreadInfo(ids, 20); + for(int i=0; i < threads.length; i++) { + ThreadInfo info=threads[i]; + if(info == null) + continue; + sb.append(info.getThreadName()).append(":\n"); + StackTraceElement[] stack_trace=info.getStackTrace(); + for(int j=0; j < stack_trace.length; j++) { + StackTraceElement el=stack_trace[j]; + sb.append("at ").append(el.getClassName()).append(".").append(el.getMethodName()); + sb.append("(").append(el.getFileName()).append(":").append(el.getLineNumber()).append(")"); + sb.append("\n"); + } + sb.append("\n\n"); + } + return sb.toString(); + } + + public static boolean interruptAndWaitToDie(Thread t) { + return interruptAndWaitToDie(t, Global.THREAD_SHUTDOWN_WAIT_TIME); + } + + public static boolean interruptAndWaitToDie(Thread t, long timeout) { + if(t == null) + throw new IllegalArgumentException("Thread can not be null"); + t.interrupt(); // interrupts the sleep() + try { + t.join(timeout); + } + catch(InterruptedException e){ + Thread.currentThread().interrupt(); // set interrupt flag again + } + return t.isAlive(); + } + + + /** + * Debugging method used to dump the content of a protocol queue in a + * condensed form. Useful to follow the evolution of the queue's content in + * time. + */ + public static String dumpQueue(Queue q) { + StringBuilder sb=new StringBuilder(); + LinkedList values=q.values(); + if(values.isEmpty()) { + sb.append("empty"); + } + else { + for(Iterator it=values.iterator(); it.hasNext();) { + Object o=it.next(); + String s=null; + if(o instanceof Event) { + Event event=(Event)o; + int type=event.getType(); + s=Event.type2String(type); + if(type == Event.VIEW_CHANGE) + s+=" " + event.getArg(); + if(type == Event.MSG) + s+=" " + event.getArg(); + + if(type == Event.MSG) { + s+="["; + Message m=(Message)event.getArg(); + Map headers=new HashMap(m.getHeaders()); + for(Iterator i=headers.keySet().iterator(); i.hasNext();) { + Object headerKey=i.next(); + Object value=headers.get(headerKey); + String headerToString=null; + if(value instanceof FD.FdHeader) { + headerToString=value.toString(); + } + else + if(value instanceof PingHeader) { + headerToString=headerKey + "-"; + if(((PingHeader)value).type == PingHeader.GET_MBRS_REQ) { + headerToString+="GMREQ"; + } + else + if(((PingHeader)value).type == PingHeader.GET_MBRS_RSP) { + headerToString+="GMRSP"; + } + else { + headerToString+="UNKNOWN"; + } + } + else { + headerToString=headerKey + "-" + (value == null ? "null" : value.toString()); + } + s+=headerToString; + + if(i.hasNext()) { + s+=","; + } + } + s+="]"; + } + } + else { + s=o.toString(); + } + sb.append(s).append("\n"); + } + } + return sb.toString(); + } + + + /** + * Use with caution: lots of overhead + */ + public static String printStackTrace(Throwable t) { + StringWriter s=new StringWriter(); + PrintWriter p=new PrintWriter(s); + t.printStackTrace(p); + return s.toString(); + } + + public static String getStackTrace(Throwable t) { + return printStackTrace(t); + } + + + + public static String print(Throwable t) { + return printStackTrace(t); + } + + + public static void crash() { + System.exit(-1); + } + + + public static String printEvent(Event evt) { + Message msg; + + if(evt.getType() == Event.MSG) { + msg=(Message)evt.getArg(); + if(msg != null) { + if(msg.getLength() > 0) + return printMessage(msg); + else + return msg.printObjectHeaders(); + } + } + return evt.toString(); + } + + + /** Tries to read an object from the message's buffer and prints it */ + public static String printMessage(Message msg) { + if(msg == null) + return ""; + if(msg.getLength() == 0) + return null; + + try { + return msg.getObject().toString(); + } + catch(Exception e) { // it is not an object + return ""; + } + } + + public static String mapToString(Map map) { + if(map == null) + return "null"; + StringBuilder sb=new StringBuilder(); + for(Map.Entry entry: map.entrySet()) { + Object key=entry.getKey(); + Object val=entry.getValue(); + sb.append(key).append("="); + if(val == null) + sb.append("null"); + else + sb.append(val); + sb.append("\n"); + } + return sb.toString(); + } + + + /** Tries to read a MethodCall object from the message's buffer and prints it. + Returns empty string if object is not a method call */ + public static String printMethodCall(Message msg) { + Object obj; + if(msg == null) + return ""; + if(msg.getLength() == 0) + return ""; + + try { + obj=msg.getObject(); + return obj.toString(); + } + catch(Exception e) { // it is not an object + return ""; + } + + } + + + public static void printThreads() { + Thread threads[]=new Thread[Thread.activeCount()]; + Thread.enumerate(threads); + System.out.println("------- Threads -------"); + for(int i=0; i < threads.length; i++) { + System.out.println("#" + i + ": " + threads[i]); + } + System.out.println("------- Threads -------\n"); + } + + + public static String activeThreads() { + StringBuilder sb=new StringBuilder(); + Thread threads[]=new Thread[Thread.activeCount()]; + Thread.enumerate(threads); + sb.append("------- Threads -------\n"); + for(int i=0; i < threads.length; i++) { + sb.append("#").append(i).append(": ").append(threads[i]).append('\n'); + } + sb.append("------- Threads -------\n"); + return sb.toString(); + } + + public static String printBytes(long bytes) { + double tmp; + + if(bytes < 1000) + return bytes + "b"; + if(bytes < 1000000) { + tmp=bytes / 1000.0; + return f.format(tmp) + "KB"; + } + if(bytes < 1000000000) { + tmp=bytes / 1000000.0; + return f.format(tmp) + "MB"; + } + else { + tmp=bytes / 1000000000.0; + return f.format(tmp) + "GB"; + } + } + + + public static String printBytes(double bytes) { + double tmp; + + if(bytes < 1000) + return bytes + "b"; + if(bytes < 1000000) { + tmp=bytes / 1000.0; + return f.format(tmp) + "KB"; + } + if(bytes < 1000000000) { + tmp=bytes / 1000000.0; + return f.format(tmp) + "MB"; + } + else { + tmp=bytes / 1000000000.0; + return f.format(tmp) + "GB"; + } + } + + + /** + Fragments a byte buffer into smaller fragments of (max.) frag_size. + Example: a byte buffer of 1024 bytes and a frag_size of 248 gives 4 fragments + of 248 bytes each and 1 fragment of 32 bytes. + @return An array of byte buffers (byte[]). + */ + public static byte[][] fragmentBuffer(byte[] buf, int frag_size, final int length) { + byte[] retval[]; + int accumulated_size=0; + byte[] fragment; + int tmp_size=0; + int num_frags; + int index=0; + + num_frags=length % frag_size == 0 ? length / frag_size : length / frag_size + 1; + retval=new byte[num_frags][]; + + while(accumulated_size < length) { + if(accumulated_size + frag_size <= length) + tmp_size=frag_size; + else + tmp_size=length - accumulated_size; + fragment=new byte[tmp_size]; + System.arraycopy(buf, accumulated_size, fragment, 0, tmp_size); + retval[index++]=fragment; + accumulated_size+=tmp_size; + } + return retval; + } + + public static byte[][] fragmentBuffer(byte[] buf, int frag_size) { + return fragmentBuffer(buf, frag_size, buf.length); + } + + + + /** + * Given a buffer and a fragmentation size, compute a list of fragmentation offset/length pairs, and + * return them in a list. Example:
        + * Buffer is 10 bytes, frag_size is 4 bytes. Return value will be ({0,4}, {4,4}, {8,2}). + * This is a total of 3 fragments: the first fragment starts at 0, and has a length of 4 bytes, the second fragment + * starts at offset 4 and has a length of 4 bytes, and the last fragment starts at offset 8 and has a length + * of 2 bytes. + * @param frag_size + * @return List. A List of offset/length pairs + */ + public static java.util.List computeFragOffsets(int offset, int length, int frag_size) { + java.util.List retval=new ArrayList(); + long total_size=length + offset; + int index=offset; + int tmp_size=0; + Range r; + + while(index < total_size) { + if(index + frag_size <= total_size) + tmp_size=frag_size; + else + tmp_size=(int)(total_size - index); + r=new Range(index, tmp_size); + retval.add(r); + index+=tmp_size; + } + return retval; + } + + public static java.util.List computeFragOffsets(byte[] buf, int frag_size) { + return computeFragOffsets(0, buf.length, frag_size); + } + + /** + Concatenates smaller fragments into entire buffers. + @param fragments An array of byte buffers (byte[]) + @return A byte buffer + */ + public static byte[] defragmentBuffer(byte[] fragments[]) { + int total_length=0; + byte[] ret; + int index=0; + + if(fragments == null) return null; + for(int i=0; i < fragments.length; i++) { + if(fragments[i] == null) continue; + total_length+=fragments[i].length; + } + ret=new byte[total_length]; + for(int i=0; i < fragments.length; i++) { + if(fragments[i] == null) continue; + System.arraycopy(fragments[i], 0, ret, index, fragments[i].length); + index+=fragments[i].length; + } + return ret; + } + + + public static void printFragments(byte[] frags[]) { + for(int i=0; i < frags.length; i++) + System.out.println('\'' + new String(frags[i]) + '\''); + } + + public static String printListWithDelimiter(Collection list, String delimiter) { + boolean first=true; + StringBuilder sb=new StringBuilder(); + for(T el: list) { + if(first) { + first=false; + } + else { + sb.append(delimiter); + } + sb.append(el); + } + return sb.toString(); + } + + + +// /** +// Peeks for view on the channel until n views have been received or timeout has elapsed. +// Used to determine the view in which we want to start work. Usually, we start as only +// member in our own view (1st view) and the next view (2nd view) will be the full view +// of all members, or a timeout if we're the first member. If a non-view (a message or +// block) is received, the method returns immediately. +// @param channel The channel used to peek for views. Has to be operational. +// @param number_of_views The number of views to wait for. 2 is a good number to ensure that, +// if there are other members, we start working with them included in our view. +// @param timeout Number of milliseconds to wait until view is forced to return. A value +// of <= 0 means wait forever. +// */ +// public static View peekViews(Channel channel, int number_of_views, long timeout) { +// View retval=null; +// Object obj=null; +// int num=0; +// long start_time=System.currentTimeMillis(); + +// if(timeout <= 0) { +// while(true) { +// try { +// obj=channel.peek(0); +// if(obj == null || !(obj instanceof View)) +// break; +// else { +// retval=(View)channel.receive(0); +// num++; +// if(num >= number_of_views) +// break; +// } +// } +// catch(Exception ex) { +// break; +// } +// } +// } +// else { +// while(timeout > 0) { +// try { +// obj=channel.peek(timeout); +// if(obj == null || !(obj instanceof View)) +// break; +// else { +// retval=(View)channel.receive(timeout); +// num++; +// if(num >= number_of_views) +// break; +// } +// } +// catch(Exception ex) { +// break; +// } +// timeout=timeout - (System.currentTimeMillis() - start_time); +// } +// } + +// return retval; +// } + + + + + public static String array2String(long[] array) { + StringBuilder ret=new StringBuilder("["); + + if(array != null) { + for(int i=0; i < array.length; i++) + ret.append(array[i]).append(" "); + } + + ret.append(']'); + return ret.toString(); + } + + public static String array2String(short[] array) { + StringBuilder ret=new StringBuilder("["); + + if(array != null) { + for(int i=0; i < array.length; i++) + ret.append(array[i]).append(" "); + } + + ret.append(']'); + return ret.toString(); + } + + public static String array2String(int[] array) { + StringBuilder ret=new StringBuilder("["); + + if(array != null) { + for(int i=0; i < array.length; i++) + ret.append(array[i]).append(" "); + } + + ret.append(']'); + return ret.toString(); + } + + public static String array2String(boolean[] array) { + StringBuilder ret=new StringBuilder("["); + + if(array != null) { + for(int i=0; i < array.length; i++) + ret.append(array[i]).append(" "); + } + ret.append(']'); + return ret.toString(); + } + + public static String array2String(Object[] array) { + StringBuilder ret=new StringBuilder("["); + + if(array != null) { + for(int i=0; i < array.length; i++) + ret.append(array[i]).append(" "); + } + ret.append(']'); + return ret.toString(); + } + + /** Returns true if all elements of c match obj */ + public static boolean all(Collection c, Object obj) { + for(Iterator iterator=c.iterator(); iterator.hasNext();) { + Object o=iterator.next(); + if(!o.equals(obj)) + return false; + } + return true; + } + + + /** + * Selects a random subset of members according to subset_percentage and returns them. + * Picks no member twice from the same membership. If the percentage is smaller than 1 -> picks 1 member. + */ + public static Vector pickSubset(Vector members, double subset_percentage) { + Vector ret=new Vector(), tmp_mbrs; + int num_mbrs=members.size(), subset_size, index; + + if(num_mbrs == 0) return ret; + subset_size=(int)Math.ceil(num_mbrs * subset_percentage); + + tmp_mbrs=(Vector)members.clone(); + + for(int i=subset_size; i > 0 && !tmp_mbrs.isEmpty(); i--) { + index=(int)((Math.random() * num_mbrs) % tmp_mbrs.size()); + ret.addElement(tmp_mbrs.elementAt(index)); + tmp_mbrs.removeElementAt(index); + } + + return ret; + } + + + public static Object pickRandomElement(List list) { + if(list == null) return null; + int size=list.size(); + int index=(int)((Math.random() * size * 10) % size); + return list.get(index); + } + + public static Object pickRandomElement(Object[] array) { + if(array == null) return null; + int size=array.length; + int index=(int)((Math.random() * size * 10) % size); + return array[index]; + } + + + /** + * Returns all members that left between 2 views. All members that are element of old_mbrs but not element of + * new_mbrs are returned. + */ + public static Vector
        determineLeftMembers(Vector
        old_mbrs, Vector
        new_mbrs) { + Vector
        retval=new Vector
        (); + Address mbr; + + if(old_mbrs == null || new_mbrs == null) + return retval; + + for(int i=0; i < old_mbrs.size(); i++) { + mbr=old_mbrs.elementAt(i); + if(!new_mbrs.contains(mbr)) + retval.addElement(mbr); + } + + return retval; + } + + + public static String printMembers(Vector v) { + StringBuilder sb=new StringBuilder("("); + boolean first=true; + Object el; + + if(v != null) { + for(int i=0; i < v.size(); i++) { + if(!first) + sb.append(", "); + else + first=false; + el=v.elementAt(i); + sb.append(el); + } + } + sb.append(')'); + return sb.toString(); + } + + + public static String printPingRsps(List rsps) { + StringBuilder sb=new StringBuilder(); + if(rsps != null) { + int total=rsps.size(); + int servers=0, clients=0, coords=0; + for(PingRsp rsp: rsps) { + if(rsp.isCoord()) + coords++; + if(rsp.isServer()) + servers++; + else + clients++; + } + sb.append(total + " total (" + servers + " servers (" + coords + " coord), " + clients + " clients)"); + } + return sb.toString(); + } + + /** + Makes sure that we detect when a peer connection is in the closed state (not closed while we send data, + but before we send data). Two writes ensure that, if the peer closed the connection, the first write + will send the peer from FIN to RST state, and the second will cause a signal (IOException). + */ + public static void doubleWrite(byte[] buf, OutputStream out) throws Exception { + if(buf.length > 1) { + out.write(buf, 0, 1); + out.write(buf, 1, buf.length - 1); + } + else { + out.write(buf, 0, 0); + out.write(buf); + } + } + + + /** + Makes sure that we detect when a peer connection is in the closed state (not closed while we send data, + but before we send data). Two writes ensure that, if the peer closed the connection, the first write + will send the peer from FIN to RST state, and the second will cause a signal (IOException). + */ + public static void doubleWrite(byte[] buf, int offset, int length, OutputStream out) throws Exception { + if(length > 1) { + out.write(buf, offset, 1); + out.write(buf, offset+1, length - 1); + } + else { + out.write(buf, offset, 0); + out.write(buf, offset, length); + } + } + + /** + * if we were to register for OP_WRITE and send the remaining data on + * readyOps for this channel we have to either block the caller thread or + * queue the message buffers that may arrive while waiting for OP_WRITE. + * Instead of the above approach this method will continuously write to the + * channel until the buffer sent fully. + */ + public static void writeFully(ByteBuffer buf, WritableByteChannel out) throws IOException { + int written = 0; + int toWrite = buf.limit(); + while (written < toWrite) { + written += out.write(buf); + } + } + +// /* double writes are not required.*/ +// public static void doubleWriteBuffer( +// ByteBuffer buf, +// WritableByteChannel out) +// throws Exception +// { +// if (buf.limit() > 1) +// { +// int actualLimit = buf.limit(); +// buf.limit(1); +// writeFully(buf,out); +// buf.limit(actualLimit); +// writeFully(buf,out); +// } +// else +// { +// buf.limit(0); +// writeFully(buf,out); +// buf.limit(1); +// writeFully(buf,out); +// } +// } + + + public static long sizeOf(String classname) { + Object inst; + byte[] data; + + try { + inst=Util.loadClass(classname, null).newInstance(); + data=Util.objectToByteBuffer(inst); + return data.length; + } + catch(Exception ex) { + return -1; + } + } + + + public static long sizeOf(Object inst) { + byte[] data; + + try { + data=Util.objectToByteBuffer(inst); + return data.length; + } + catch(Exception ex) { + return -1; + } + } + + public static int sizeOf(Streamable inst) { + byte[] data; + ByteArrayOutputStream output; + DataOutputStream out; + + try { + output=new ByteArrayOutputStream(); + out=new DataOutputStream(output); + inst.writeTo(out); + out.flush(); + data=output.toByteArray(); + return data.length; + } + catch(Exception ex) { + return -1; + } + } + + + + /** + * Tries to load the class from the current thread's context class loader. If + * not successful, tries to load the class from the current instance. + * @param classname Desired class. + * @param clazz Class object used to obtain a class loader + * if no context class loader is available. + * @return Class, or null on failure. + */ + public static Class loadClass(String classname, Class clazz) throws ClassNotFoundException { + ClassLoader loader; + + try { + loader=Thread.currentThread().getContextClassLoader(); + if(loader != null) { + return loader.loadClass(classname); + } + } + catch(Throwable t) { + } + + if(clazz != null) { + try { + loader=clazz.getClassLoader(); + if(loader != null) { + return loader.loadClass(classname); + } + } + catch(Throwable t) { + } + } + + try { + loader=ClassLoader.getSystemClassLoader(); + if(loader != null) { + return loader.loadClass(classname); + } + } + catch(Throwable t) { + } + + throw new ClassNotFoundException(classname); + } + + + public static InputStream getResourceAsStream(String name, Class clazz) { + ClassLoader loader; + InputStream retval=null; + + try { + loader=Thread.currentThread().getContextClassLoader(); + if(loader != null) { + retval=loader.getResourceAsStream(name); + if(retval != null) + return retval; + } + } + catch(Throwable t) { + } + + if(clazz != null) { + try { + loader=clazz.getClassLoader(); + if(loader != null) { + retval=loader.getResourceAsStream(name); + if(retval != null) + return retval; + } + } + catch(Throwable t) { + } + } + + try { + loader=ClassLoader.getSystemClassLoader(); + if(loader != null) { + return loader.getResourceAsStream(name); + } + } + catch(Throwable t) { + } + + return retval; + } + + + /** Checks whether 2 Addresses are on the same host */ + public static boolean sameHost(Address one, Address two) { + InetAddress a, b; + String host_a, host_b; + + if(one == null || two == null) return false; + if(!(one instanceof IpAddress) || !(two instanceof IpAddress)) { + return false; + } + + a=((IpAddress)one).getIpAddress(); + b=((IpAddress)two).getIpAddress(); + if(a == null || b == null) return false; + host_a=a.getHostAddress(); + host_b=b.getHostAddress(); + + // System.out.println("host_a=" + host_a + ", host_b=" + host_b); + return host_a.equals(host_b); + } + + + + public static boolean fileExists(String fname) { + return (new File(fname)).exists(); + } + + + /** + * Parses comma-delimited longs; e.g., 2000,4000,8000. + * Returns array of long, or null. + */ + public static long[] parseCommaDelimitedLongs(String s) { + StringTokenizer tok; + Vector v=new Vector(); + Long l; + long[] retval=null; + + if(s == null) return null; + tok=new StringTokenizer(s, ","); + while(tok.hasMoreTokens()) { + l=new Long(tok.nextToken()); + v.addElement(l); + } + if(v.isEmpty()) return null; + retval=new long[v.size()]; + for(int i=0; i < v.size(); i++) + retval[i]=((Long)v.elementAt(i)).longValue(); + return retval; + } + + /** e.g. "bela,jeannette,michelle" --> List{"bela", "jeannette", "michelle"} */ + public static List parseCommaDelimitedStrings(String l) { + return parseStringList(l, ","); + } + + + public static List parseStringList(String l, String separator) { + List tmp=new LinkedList(); + StringTokenizer tok=new StringTokenizer(l, separator); + String t; + + while(tok.hasMoreTokens()) { + t=tok.nextToken(); + tmp.add(t.trim()); + } + + return tmp; + } + + public static int parseInt(Properties props,String property,int defaultValue) + { + int result = defaultValue; + String str=props.getProperty(property); + if(str != null) { + result=Integer.parseInt(str); + props.remove(property); + } + return result; + } + + + public static long parseLong(Properties props,String property,long defaultValue) + { + long result = defaultValue; + String str=props.getProperty(property); + if(str != null) { + result=Integer.parseInt(str); + props.remove(property); + } + return result; + } + + public static boolean parseBoolean(Properties props,String property,boolean defaultValue) + { + boolean result = defaultValue; + String str=props.getProperty(property); + if(str != null) { + result=str.equalsIgnoreCase("true"); + props.remove(property); + } + return result; + } + + public static InetAddress parseBindAddress(Properties props, String property) throws UnknownHostException { + InetAddress bind_addr=null; + boolean ignore_systemprops=Util.isBindAddressPropertyIgnored(); + String str=Util.getProperty(new String[]{Global.BIND_ADDR, Global.BIND_ADDR_OLD}, props, "bind_addr", + ignore_systemprops, null); + if(str != null) { + bind_addr=InetAddress.getByName(str); + props.remove(property); + } + return bind_addr; + } + + + /** + * + * @param s + * @return List + */ + public static List parseInterfaceList(String s) throws Exception { + List interfaces=new ArrayList(10); + if(s == null) + return null; + + StringTokenizer tok=new StringTokenizer(s, ","); + String interface_name; + NetworkInterface intf; + + while(tok.hasMoreTokens()) { + interface_name=tok.nextToken(); + + // try by name first (e.g. (eth0") + intf=NetworkInterface.getByName(interface_name); + + // next try by IP address or symbolic name + if(intf == null) + intf=NetworkInterface.getByInetAddress(InetAddress.getByName(interface_name)); + + if(intf == null) + throw new Exception("interface " + interface_name + " not found"); + if(!interfaces.contains(intf)) { + interfaces.add(intf); + } + } + return interfaces; + } + + + public static String print(List interfaces) { + StringBuilder sb=new StringBuilder(); + boolean first=true; + + for(NetworkInterface intf: interfaces) { + if(first) { + first=false; + } + else { + sb.append(", "); + } + sb.append(intf.getName()); + } + return sb.toString(); + } + + + public static String shortName(String hostname) { + int index; + StringBuilder sb=new StringBuilder(); + + if(hostname == null) return null; + + index=hostname.indexOf('.'); + if(index > 0 && !Character.isDigit(hostname.charAt(0))) + sb.append(hostname.substring(0, index)); + else + sb.append(hostname); + return sb.toString(); + } + + public static String shortName(InetAddress hostname) { + if(hostname == null) return null; + StringBuilder sb=new StringBuilder(); + if(resolve_dns) + sb.append(hostname.getHostName()); + else + sb.append(hostname.getHostAddress()); + return sb.toString(); + } + + public static boolean startFlush(Channel c, List
        flushParticipants, int numberOfAttempts, long randomSleepTimeoutFloor,long randomSleepTimeoutCeiling) { + boolean successfulFlush = false; + int attemptCount = 0; + while(attemptCount < numberOfAttempts){ + successfulFlush = c.startFlush(flushParticipants, false); + if(successfulFlush) + break; + Util.sleepRandom(randomSleepTimeoutFloor,randomSleepTimeoutCeiling); + attemptCount++; + } + return successfulFlush; + } + + public static boolean startFlush(Channel c, List
        flushParticipants) { + return startFlush(c,flushParticipants,4,1000,5000); + } + + public static boolean startFlush(Channel c, int numberOfAttempts, long randomSleepTimeoutFloor,long randomSleepTimeoutCeiling) { + boolean successfulFlush = false; + int attemptCount = 0; + while(attemptCount < numberOfAttempts){ + successfulFlush = c.startFlush(false); + if(successfulFlush) + break; + Util.sleepRandom(randomSleepTimeoutFloor,randomSleepTimeoutCeiling); + attemptCount++; + } + return successfulFlush; + } + + public static boolean startFlush(Channel c) { + return startFlush(c,4,1000,5000); + } + + + /** Finds first available port starting at start_port and returns server socket */ + public static ServerSocket createServerSocket(int start_port) throws IOException{ + ServerSocket ret=null; + + while(true) { + try { + ret=new ServerSocket(start_port); + } + catch(BindException bind_ex) { + start_port++; + continue; + } + catch(IOException io_ex) { + } + break; + } + if(ret == null) + throw new IOException("Could not create server socket, start port:" +start_port); + return ret; + } + + public static ServerSocket createServerSocket(InetAddress bind_addr, int start_port) throws IOException{ + ServerSocket ret=null; + + while(true) { + try { + ret=new ServerSocket(start_port, 50, bind_addr); + } + catch(BindException bind_ex) { + start_port++; + continue; + } + catch(IOException io_ex) { + } + break; + } + if(ret == null) + throw new IOException("Could not create server socket, start port:" +start_port); + return ret; + } + + + + /** + * Creates a DatagramSocket bound to addr. If addr is null, socket won't be bound. If address is already in use, + * start_port will be incremented until a socket can be created. + * @param addr The InetAddress to which the socket should be bound. If null, the socket will not be bound. + * @param port The port which the socket should use. If 0, a random port will be used. If > 0, but port is already + * in use, it will be incremented until an unused port is found, or until MAX_PORT is reached. + */ + public static DatagramSocket createDatagramSocket(InetAddress addr, int port) throws Exception { + DatagramSocket sock=null; + + if(addr == null) { + if(port == 0) { + return new DatagramSocket(); + } + else { + while(port < MAX_PORT) { + try { + return new DatagramSocket(port); + } + catch(BindException bind_ex) { // port already used + port++; + } + catch(Exception ex) { + throw ex; + } + } + } + } + else { + if(port == 0) port=1024; + while(port < MAX_PORT) { + try { + return new DatagramSocket(port, addr); + } + catch(BindException bind_ex) { // port already used + port++; + } + catch(Exception ex) { + throw ex; + } + } + } + if(sock == null) + throw new IOException ("Could not create udp socket, port = "+ port); + return sock; // will never be reached, but the stupid compiler didn't figure it out... + } + + + public static MulticastSocket createMulticastSocket(int port) throws IOException { + return createMulticastSocket(null, port, null); + } + + public static MulticastSocket createMulticastSocket(InetAddress mcast_addr, int port, Log log) throws IOException { + if(mcast_addr != null && !mcast_addr.isMulticastAddress()) { + if(log != null && log.isWarnEnabled()) + log.warn("mcast_addr (" + mcast_addr + ") is not a multicast address, will be ignored"); + return new MulticastSocket(port); + } + + SocketAddress saddr=new InetSocketAddress(mcast_addr, port); + MulticastSocket retval=null; + + try { + retval=new MulticastSocket(saddr); + } + catch(IOException ex) { + if(log != null && log.isWarnEnabled()) { + StringBuilder sb=new StringBuilder(); + String type=mcast_addr != null ? mcast_addr instanceof Inet4Address? "IPv4" : "IPv6" : "n/a"; + sb.append("could not bind to " + mcast_addr + " (" + type + " address)"); + sb.append("; make sure your mcast_addr is of the same type as the IP stack (IPv4 or IPv6)."); + sb.append("\nWill ignore mcast_addr, but this may lead to cross talking " + + "(see http://www.jboss.com/wiki/Edit.jsp?page=CrossTalking for details). "); + sb.append("\nException was: " + ex); + log.warn(sb); + } + } + if(retval == null) + retval=new MulticastSocket(port); + return retval; + } + + + + /** + * Returns the address of the interface to use defined by bind_addr and bind_interface + * @param props + * @return + * @throws UnknownHostException + * @throws SocketException + */ + public static InetAddress getBindAddress(Properties props) throws UnknownHostException, SocketException { + boolean ignore_systemprops=Util.isBindAddressPropertyIgnored(); + String bind_addr=Util.getProperty(new String[]{Global.BIND_ADDR, Global.BIND_ADDR_OLD}, props, "bind_addr", + ignore_systemprops, null); + String bind_interface=Util.getProperty(new String[]{Global.BIND_INTERFACE, null}, props, "bind_interface", + ignore_systemprops, null); + InetAddress retval=null, bind_addr_host=null; + + if(bind_addr != null) { + bind_addr_host=InetAddress.getByName(bind_addr); + } + + if(bind_interface != null) { + NetworkInterface intf=NetworkInterface.getByName(bind_interface); + if(intf != null) { + for(Enumeration addresses=intf.getInetAddresses(); addresses.hasMoreElements();) { + InetAddress addr=addresses.nextElement(); + if(bind_addr == null) { + retval=addr; + break; + } + else { + if(bind_addr_host != null) { + if(bind_addr_host.equals(addr)) { + retval=addr; + break; + } + } + else if(addr.getHostAddress().trim().equalsIgnoreCase(bind_addr)) { + retval=addr; + break; + } + } + } + } + else { + throw new UnknownHostException("network interface " + bind_interface + " not found"); + } + } + + if(retval == null) { + retval=bind_addr != null? InetAddress.getByName(bind_addr) : InetAddress.getLocalHost(); + } + + props.remove("bind_addr"); + props.remove("bind_interface"); + return retval; + } + + + public static boolean checkForLinux() { + return checkForPresence("os.name", "linux"); + } + + public static boolean checkForSolaris() { + return checkForPresence("os.name", "sun"); + } + + public static boolean checkForWindows() { + return checkForPresence("os.name", "win"); + } + + private static boolean checkForPresence(String key, String value) { + try { + String tmp=System.getProperty(key); + return tmp != null && tmp.trim().toLowerCase().startsWith(value); + } + catch(Throwable t) { + return false; + } + } + + public static void prompt(String s) { + System.out.println(s); + System.out.flush(); + try { + while(System.in.available() > 0) + System.in.read(); + System.in.read(); + } + catch(IOException e) { + e.printStackTrace(); + } + } + + + public static int getJavaVersion() { + String version=System.getProperty("java.version"); + int retval=0; + if(version != null) { + if(version.startsWith("1.2")) + return 12; + if(version.startsWith("1.3")) + return 13; + if(version.startsWith("1.4")) + return 14; + if(version.startsWith("1.5")) + return 15; + if(version.startsWith("5")) + return 15; + if(version.startsWith("1.6")) + return 16; + if(version.startsWith("6")) + return 16; + } + return retval; + } + + public static Vector unmodifiableVector(Vector v) { + if(v == null) return null; + return new UnmodifiableVector(v); + } + + public static String memStats(boolean gc) { + StringBuilder sb=new StringBuilder(); + Runtime rt=Runtime.getRuntime(); + if(gc) + rt.gc(); + long free_mem, total_mem, used_mem; + free_mem=rt.freeMemory(); + total_mem=rt.totalMemory(); + used_mem=total_mem - free_mem; + sb.append("Free mem: ").append(free_mem).append("\nUsed mem: ").append(used_mem); + sb.append("\nTotal mem: ").append(total_mem); + return sb.toString(); + } + + +// public static InetAddress getFirstNonLoopbackAddress() throws SocketException { +// Enumeration en=NetworkInterface.getNetworkInterfaces(); +// while(en.hasMoreElements()) { +// NetworkInterface i=(NetworkInterface)en.nextElement(); +// for(Enumeration en2=i.getInetAddresses(); en2.hasMoreElements();) { +// InetAddress addr=(InetAddress)en2.nextElement(); +// if(!addr.isLoopbackAddress()) +// return addr; +// } +// } +// return null; +// } + + + public static InetAddress getFirstNonLoopbackAddress() throws SocketException { + Enumeration en=NetworkInterface.getNetworkInterfaces(); + boolean preferIpv4=Boolean.getBoolean("java.net.preferIPv4Stack"); + boolean preferIPv6=Boolean.getBoolean("java.net.preferIPv6Addresses"); + while(en.hasMoreElements()) { + NetworkInterface i=(NetworkInterface)en.nextElement(); + for(Enumeration en2=i.getInetAddresses(); en2.hasMoreElements();) { + InetAddress addr=(InetAddress)en2.nextElement(); + if(!addr.isLoopbackAddress()) { + if(addr instanceof Inet4Address) { + if(preferIPv6) + continue; + return addr; + } + if(addr instanceof Inet6Address) { + if(preferIpv4) + continue; + return addr; + } + } + } + } + return null; + } + + + + public static InetAddress getFirstNonLoopbackIPv6Address() throws SocketException { + Enumeration en=NetworkInterface.getNetworkInterfaces(); + while(en.hasMoreElements()) { + NetworkInterface i=(NetworkInterface)en.nextElement(); + for(Enumeration en2=i.getInetAddresses(); en2.hasMoreElements();) { + InetAddress addr=(InetAddress)en2.nextElement(); + if(!addr.isLoopbackAddress()) { + if(addr instanceof Inet4Address) { + continue; + } + if(addr instanceof Inet6Address) { + return addr; + } + } + } + } + return null; + } + + public static List getAllAvailableInterfaces() throws SocketException { + List retval=new ArrayList(10); + NetworkInterface intf; + for(Enumeration en=NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) { + intf=(NetworkInterface)en.nextElement(); + retval.add(intf); + } + return retval; + } + + + /** + * Returns a value associated wither with one or more system properties, or found in the props map + * @param system_props + * @param props List of properties read from the configuration file + * @param prop_name The name of the property, will be removed from props if found + * @param ignore_sysprops If true, system properties are not used and the values will only be retrieved from + * props (not system_props) + * @param default_value Used to return a default value if the properties or system properties didn't have the value + * @return The value, or null if not found + */ + public static String getProperty(String[] system_props, Properties props, String prop_name, + boolean ignore_sysprops, String default_value) { + String retval=null; + if(props != null && prop_name != null) { + retval=props.getProperty(prop_name); + props.remove(prop_name); + } + + if(!ignore_sysprops) { + String tmp, prop; + if(system_props != null) { + for(int i=0; i < system_props.length; i++) { + prop=system_props[i]; + if(prop != null) { + try { + tmp=System.getProperty(prop); + if(tmp != null) + return tmp; // system properties override config file definitions + } + catch(SecurityException ex) {} + } + } + } + } + if(retval == null) + return default_value; + return retval; + } + + + + public static boolean isBindAddressPropertyIgnored() { + try { + String tmp=System.getProperty(Global.IGNORE_BIND_ADDRESS_PROPERTY); + if(tmp == null) { + tmp=System.getProperty(Global.IGNORE_BIND_ADDRESS_PROPERTY_OLD); + if(tmp == null) + return false; + } + tmp=tmp.trim().toLowerCase(); + return !(tmp.equals("false") || tmp.equals("no") || tmp.equals("off")) && (tmp.equals("true") || tmp.equals("yes") || tmp.equals("on")); + } + catch(SecurityException ex) { + return false; + } + } + + + public static MBeanServer getMBeanServer() { + ArrayList servers = MBeanServerFactory.findMBeanServer(null); + if (servers != null && !servers.isEmpty()) { + // return 'jboss' server if available + for (int i = 0; i < servers.size(); i++) { + MBeanServer srv = (MBeanServer) servers.get(i); + if ("jboss".equalsIgnoreCase(srv.getDefaultDomain())) + return srv; + } + + // return first available server + return (MBeanServer) servers.get(0); + } + else { + //if it all fails, get the default server + return ManagementFactory.getPlatformMBeanServer(); + } + } + + + public static String getProperty(Protocol prot, String prop_name) { + if(prot == null) + return null; + String name=prot.getProperties().getProperty(prop_name); + return name == null? name : name.trim(); + } + + + /* + public static void main(String[] args) { + DatagramSocket sock; + InetAddress addr=null; + int port=0; + + for(int i=0; i < args.length; i++) { + if(args[i].equals("-help")) { + System.out.println("Util [-help] [-addr] [-port]"); + return; + } + if(args[i].equals("-addr")) { + try { + addr=InetAddress.getByName(args[++i]); + continue; + } + catch(Exception ex) { + log.error(ex); + return; + } + } + if(args[i].equals("-port")) { + port=Integer.parseInt(args[++i]); + continue; + } + System.out.println("Util [-help] [-addr] [-port]"); + return; + } + + try { + sock=createDatagramSocket(addr, port); + System.out.println("sock: local address is " + sock.getLocalAddress() + ":" + sock.getLocalPort() + + ", remote address is " + sock.getInetAddress() + ":" + sock.getPort()); + System.in.read(); + } + catch(Exception ex) { + log.error(ex); + } + } + */ + + public static void main(String args[]) throws Exception { + ClassConfigurator.getInstance(true); + + Message msg=new Message(null, new IpAddress("127.0.0.1", 4444), "Bela"); + int size=Util.sizeOf(msg); + System.out.println("size=" + msg.size() + ", streamable size=" + size); + + msg.putHeader("belaban", new NakAckHeader((byte)1, 23, 34)); + size=Util.sizeOf(msg); + System.out.println("size=" + msg.size() + ", streamable size=" + size); + + msg.putHeader("bla", new UdpHeader("groupname")); + size=Util.sizeOf(msg); + System.out.println("size=" + msg.size() + ", streamable size=" + size); + + + IpAddress a1=new IpAddress(1234), a2=new IpAddress("127.0.0.1", 3333); + a1.setAdditionalData("Bela".getBytes()); + size=Util.sizeOf(a1); + System.out.println("size=" + a1.size() + ", streamable size of a1=" + size); + size=Util.sizeOf(a2); + System.out.println("size=" + a2.size() + ", streamable size of a2=" + size); + + +// System.out.println("Check for Linux: " + checkForLinux()); +// System.out.println("Check for Solaris: " + checkForSolaris()); +// System.out.println("Check for Windows: " + checkForWindows()); +// System.out.println("version: " + getJavaVersion()); + } + + + public static String generateList(Collection c, String separator) { + if(c == null) return null; + StringBuilder sb=new StringBuilder(); + boolean first=true; + + for(Iterator it=c.iterator(); it.hasNext();) { + if(first) { + first=false; + } + else { + sb.append(separator); + } + sb.append(it.next()); + } + return sb.toString(); + } + + /** + * Replaces variables of ${var:default} with System.getProperty(var, default). If no variables are found, returns + * the same string, otherwise a copy of the string with variables substituted + * @param val + * @return A string with vars replaced, or the same string if no vars found + */ + public static String substituteVariable(String val) { + if(val == null) + return val; + String retval=val, prev; + + while(retval.contains("${")) { // handle multiple variables in val + prev=retval; + retval=_substituteVar(retval); + if(retval.equals(prev)) + break; + } + return retval; + } + + private static String _substituteVar(String val) { + int start_index, end_index; + start_index=val.indexOf("${"); + if(start_index == -1) + return val; + end_index=val.indexOf("}", start_index+2); + if(end_index == -1) + throw new IllegalArgumentException("missing \"}\" in " + val); + + String tmp=getProperty(val.substring(start_index +2, end_index)); + if(tmp == null) + return val; + StringBuilder sb=new StringBuilder(); + sb.append(val.substring(0, start_index)); + sb.append(tmp); + sb.append(val.substring(end_index+1)); + return sb.toString(); + } + + private static String getProperty(String s) { + String var, default_val, retval=null; + int index=s.indexOf(":"); + if(index >= 0) { + var=s.substring(0, index); + default_val=s.substring(index+1); + retval=System.getProperty(var, default_val); + } + else { + var=s; + retval=System.getProperty(var); + } + return retval; + } + + + /** + * Used to convert a byte array in to a java.lang.String object + * @param bytes the bytes to be converted + * @return the String representation + */ + private static String getString(byte[] bytes) { + StringBuilder sb=new StringBuilder(); + for (int i = 0; i < bytes.length; i++) { + byte b = bytes[i]; + sb.append(0x00FF & b); + if (i + 1 < bytes.length) { + sb.append("-"); + } + } + return sb.toString(); + } + + + /** + * Converts a java.lang.String in to a MD5 hashed String + * @param source the source String + * @return the MD5 hashed version of the string + */ + public static String md5(String source) { + try { + MessageDigest md = MessageDigest.getInstance("MD5"); + byte[] bytes = md.digest(source.getBytes()); + return getString(bytes); + } catch (Exception e) { + return null; + } + } + /** + * Converts a java.lang.String in to a SHA hashed String + * @param source the source String + * @return the MD5 hashed version of the string + */ + public static String sha(String source) { + try { + MessageDigest md = MessageDigest.getInstance("SHA"); + byte[] bytes = md.digest(source.getBytes()); + return getString(bytes); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + +} + + + + + + Index: 3rdParty_sources/jgroups/org/jgroups/util/package.html =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/package.html,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/package.html 17 Aug 2012 14:51:02 -0000 1.1 @@ -0,0 +1,5 @@ + + + Provides useful functionality which cannot be assigned to any particular other package. + + \ No newline at end of file Index: 3rdParty_sources/jgroups/org/jgroups/util/todo.txt =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/todo.txt,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/todo.txt 17 Aug 2012 14:51:01 -0000 1.1 @@ -0,0 +1,80 @@ + + Todo List + ========= + +$Id: todo.txt,v 1.1 2012/08/17 14:51:01 marcin Exp $ + + +Currently Proxy resides on the server system, listens on multiple +ports (as defined in mapping file) and forwards incoming connections +to correct destinations. + + +1. Destination (host:port) as part of message +--------------------------------------------- + +Currently the server takes incoming requests and routes them to +destinations according to the specified mapping. +However it should be possible to include the destination with an +incoming request: when the proxy accepts a new connection it needs to +check whether there is a destination directive. If not, it forwards +the request according to the mapping table, otherwise it uses the +destination information shipped with the request to route the +message. + +If dynamic destination information is used, we will be able to listen +on a single server socket only; all clients send their request +(including the destination it should be forwarded to) to the server, +and the server then handles the request in a separate thread. Note +that the client side has to be instrumented to include dynamic +forwarding information with each request (see Client Proxy). + + +2. Client Proxy +--------------- + +When a client is behind a firewall, it is often not possible to create +(TCP) connections to hosts outside the firewall on ports other than +HTTP (80). To overcome this problem we create local server sockets +which accept requests and route them to port 80 on a remote server +machine. Each server socket is configured with a local port (on which +to listen) and a remote destination to which all requests should be +sent to. When receiving a request the forwarding destination is +included with the message and sent to port 80 of the outside +machine. The server proxy then forwards the request according to the +routing destination included in the message. + + + + +2.1. Example: mapping table client side +--------------------------------------- + +localhost:8110=pop.mail.yahoo.com:110 remote.host.com:80 +localhost:2200=cvs.sf.net:22 remote.host.com:80 (ssl) + +The first line creates a server socket on the localhost at port +8110. Every incoming request will be forwarded to remote.host.com port +80 and will include a forwarding destination of +pop.mail.yahoo.com:110. This means the proxy on the server side +will receive a connection on port 80. It extracts the header, which +contains the pop.mail.yahoo.com:110 destination and forwards the +request to Yahoo. The response is then returned via the regular way. + +2.1.1 SOCKS support +------------------- + +In the above example we established a direct TCP connection to +remote.host.com:80. However, if we have a firewall, and all +outgoing/incoming traffic is blocked, and we have to use SOCKS(5) to +get outside, then this direct connection will fail. Therefore we need +to enable SOCKS support in the Client Proxy by telling the VM about +the SOCKS server and port. + +Fortunately this does not require any code changes, but simply the +setting of some system properties. How to do is id described in +http://java.sun.com/j2se/1.4/docs/guide/net/properties.html. + + + + Index: 3rdParty_sources/jgroups/org/jgroups/util/obsolete/CondVar.java.txt =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/obsolete/CondVar.java.txt,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/obsolete/CondVar.java.txt 17 Aug 2012 14:51:25 -0000 1.1 @@ -0,0 +1,130 @@ +package org.jgroups.util; + +import org.jgroups.TimeoutException; + + +/** + * Class that checks on a condition and - if condition doesn't match the expected result - waits until the result + * matches the expected result, or a timeout occurs. First version used WaitableBoolean from util.concurrent, but + * that class would not allow for timeouts. + * @author Bela Ban + * @version $Id: CondVar.java.txt,v 1.1 2012/08/17 14:51:25 marcin Exp $ + */ +public class CondVar { + Object cond; + final String name; + final Object lock; + + public CondVar(String name, Object cond) { + this.name=name; + this.cond=cond; + lock=this; + } + + public CondVar(String name, Object cond, Object lock) { + this.name=name; + this.cond=cond; + this.lock=lock; + } + + public Object get() { + synchronized(lock) { + return cond; + } + } + + /** Sets the result */ + public void set(Object result) { + synchronized(lock) { + cond=result; + lock.notifyAll(); + } + } + + public Object getLock() { + return lock; + } + + + /** + * Waits until the condition matches the expected result. Returns immediately if they match, otherwise waits + * for timeout milliseconds or until the results match. + * @param result The result, needs to match the condition (using equals()). + * @param timeout Number of milliseconds to wait. A value of <= 0 means to wait forever + * @throws TimeoutException Thrown if the result still doesn't match the condition after timeout + * milliseconds have elapsed + */ + public void waitUntilWithTimeout(Object result, long timeout) throws TimeoutException { + _waitUntilWithTimeout(result, timeout); + } + + /** + * Waits until the condition matches the expected result. Returns immediately if they match, otherwise waits + * for timeout milliseconds or until the results match. This method doesn't throw a TimeoutException + * @param result The result, needs to match the condition (using equals()). + * @param timeout Number of milliseconds to wait. A value of <= 0 means to wait forever + */ + public void waitUntil(Object result, long timeout) { + try { + _waitUntilWithTimeout(result, timeout); + } + catch(TimeoutException e) { + + } + } + + public void waitUntil(Object result) { + try {waitUntilWithTimeout(result, 0);} catch(TimeoutException e) {} + } + + + + private void _waitUntilWithTimeout(Object result, long timeout) throws TimeoutException { + long time_to_wait=timeout, start; + boolean timeout_occurred=false; + synchronized(lock) { + if(result == null && cond == null) return; + + start=System.currentTimeMillis(); + while(match(result, cond) == false) { + if(timeout <= 0) { + doWait(); + } + else { + if(time_to_wait <= 0) { + timeout_occurred=true; + break; // terminate the while loop + } + else { + doWait(time_to_wait); + time_to_wait=timeout - (System.currentTimeMillis() - start); + } + } + } + if(timeout_occurred) + throw new TimeoutException(); + } + } + + + + + void doWait() { + try {lock.wait();} catch(InterruptedException e) {} + } + + void doWait(long timeout) { + try {lock.wait(timeout);} catch(InterruptedException e) {} + } + + private boolean match(Object o1, Object o2) { + if(o1 != null) + return o1.equals(o2); + else + return o2.equals(o1); + } + + public String toString() { + return name + "=" + cond; + } +} Index: 3rdParty_sources/jgroups/org/jgroups/util/obsolete/List.java.txt =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/jgroups/org/jgroups/util/obsolete/List.java.txt,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/jgroups/org/jgroups/util/obsolete/List.java.txt 17 Aug 2012 14:51:25 -0000 1.1 @@ -0,0 +1,482 @@ +// $Id: List.java.txt,v 1.1 2012/08/17 14:51:25 marcin Exp $ + +package org.jgroups.util; + +import java.io.*; +import java.util.*; + + +/** + * Doubly-linked list. Elements can be added at head or tail and removed from head/tail. + * This class is tuned for element access at either head or tail, random access to elements + * is not very fast; in this case use Vector. Concurrent access is supported: a thread is blocked + * while another thread adds/removes an object. When no objects are available, removal returns null. + * @author Bela Ban + */ +public class List implements Externalizable, Cloneable { + protected Element head=null, tail=null; + protected int size=0; + protected transient final Object mutex=new Object(); + + + + class Element { + Object obj=null; + Element next=null; + Element prev=null; + + Element(Object o) { + obj=o; + } + } + + + public List() { + } + + + /** + Adds an object at the tail of the list. + */ + public void add(Object obj) { + Element el=new Element(obj); + + synchronized(mutex) { + if(head == null) { + head=el; + tail=head; + size=1; + } + else { + el.prev=tail; + tail.next=el; + tail=el; + size++; + } + } + } + + /** + Adds an object at the head of the list. + */ + public void addAtHead(Object obj) { + Element el=new Element(obj); + + synchronized(mutex) { + if(head == null) { + head=el; + tail=head; + size=1; + } + else { + el.next=head; + head.prev=el; + head=el; + size++; + } + } + } + + + public void addAll(Collection c) { + if(c == null) + return; + for(Iterator it=c.iterator(); it.hasNext();) { + add(it.next()); + } + } + + + /** + Removes an object from the tail of the list. Returns null if no elements available + */ + public Object remove() { + Element retval=null; + + synchronized(mutex) { + if(tail == null) + return null; + retval=tail; + if(head == tail) { // last element + head=null; + tail=null; + } + else { + tail.prev.next=null; + tail=tail.prev; + retval.prev=null; + } + + size--; + } + return retval.obj; + } + + + /** Removes an object from the head of the list. Returns null if no elements available */ + public Object removeFromHead() { + Element retval=null; + + synchronized(mutex) { + if(head == null) + return null; + retval=head; + if(head == tail) { // last element + head=null; + tail=null; + } + else { + head=head.next; + head.prev=null; + retval.next=null; + } + size--; + } + return retval.obj; + } + + + /** + Returns element at the tail (if present), but does not remove it from list. + */ + public Object peek() { + synchronized(mutex) { + return tail != null ? tail.obj : null; + } + } + + + /** + Returns element at the head (if present), but does not remove it from list. + */ + public Object peekAtHead() { + synchronized(mutex) { + return head != null ? head.obj : null; + } + } + + + /** + Removes element obj from the list, checking for equality using the equals + operator. Only the first duplicate object is removed. Returns the removed object. + */ + public Object removeElement(Object obj) { + Element el=null; + Object retval=null; + + synchronized(mutex) { + el=head; + while(el != null) { + if(el.obj.equals(obj)) { + retval=el.obj; + if(head == tail) { // only 1 element left in the list + head=null; + tail=null; + } + else + if(el.prev == null) { // we're at the head + head=el.next; + head.prev=null; + el.next=null; + } + else + if(el.next == null) { // we're at the tail + tail=el.prev; + tail.next=null; + el.prev=null; + } + else { // we're somewhere in the middle of the list + el.prev.next=el.next; + el.next.prev=el.prev; + el.next=null; + el.prev=null; + } + size--; + break; + } + + el=el.next; + } + } + return retval; + } + + + public void removeAll() { + synchronized(mutex) { + size=0; + head=null; + tail=null; + } + } + + + public int size() { + return size; + } + + public String toString() { + StringBuilder ret=new StringBuilder("["); + Element el=head; + + while(el != null) { + if(el.obj != null) + ret.append(el.obj).append(" "); + el=el.next; + } + ret.append(']'); + return ret.toString(); + } + + public String toStringWithDelimiter(String delimiter) { + Element el=head; + boolean first=true; + StringBuilder sb=new StringBuilder(); + while(el != null) { + if(first) { + first=false; + } + else { + sb.append(delimiter); + } + sb.append(el.obj); + el=el.next; + } + return sb.toString(); + } + + + public String dump() { + StringBuilder ret=new StringBuilder("["); + for(Element el=head; el != null; el=el.next) + ret.append(el.obj).append(" "); + + return ret.toString() + ']'; + } + + + public Vector getContents() { + Vector retval=new Vector(size); + Element el; + + synchronized(mutex) { + el=head; + while(el != null) { + retval.addElement(el.obj); + el=el.next; + } + } + return retval; + } + + + public Enumeration elements() { + return new ListEnumerator(head); + } + + + public boolean contains(Object obj) { + Element el=head; + + while(el != null) { + if(el.obj != null && el.obj.equals(obj)) + return true; + el=el.next; + } + return false; + } + + + public List copy() { + List retval=new List(); + + synchronized(mutex) { + for(Element el=head; el != null; el=el.next) + retval.add(el.obj); + } + return retval; + } + + + protected Object clone() throws CloneNotSupportedException { + // calling clone() is superfluous because we don't want a shallow copy + return copy(); + } + + + + public void writeExternal(ObjectOutput out) throws IOException { + Element el; + + synchronized(mutex) { + el=head; + out.writeInt(size); + for(int i=0; i < size; i++) { + out.writeObject(el.obj); + el=el.next; + } + } + } + + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + Object obj; + int new_size=in.readInt(); + + if(new_size == 0) + return; + for(int i=0; i < new_size; i++) { + obj=in.readObject(); + add(obj); + } + } + + +// public void writeTo(DataOutputStream out) throws IOException { +// Element el; +// Object obj; +// if(size == 0) { +// out.writeInt(0); +// return; +// } +// out.writeInt(size); +// el=head; +// while(el != null) { +// obj=el.obj; +// if(obj instanceof Streamable) { +// out.writeByte(1); +// ((Streamable)obj).writeTo(out); +// } +// else { +// out.writeByte(0); +// ObjectOutputStream oos=new ObjectOutputStream(out); // very inefficient +// oos.writeObject(obj); +// oos.close(); +// } +// el=el.next; +// } +// } +// +// public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { +// Object obj; +// int size=in.readInt(); +// byte b; +// +// for(int i=0; i < size; i++) { +// b=in.readByte(); +// if(b == 1) { +// +// } +// else if(b == 0) { +// +// } +// else +// throw new InstantiationException("byte '" + b + "' not recognized (needs to be 1 or 0)"); +// } +// +// } + + + + + class ListEnumerator implements Enumeration { + Element curr=null; + + ListEnumerator(Element start) { + curr=start; + } + + public boolean hasMoreElements() { + return curr != null; + } + + public Object nextElement() { + Object retval; + + if(curr == null) + throw new NoSuchElementException(); + retval=curr.obj; + curr=curr.next; + return retval; + } + + } + + + + +// public static void main(String args[]) { +// List l=new List(); + +// l.add("Bela"); +// l.add("Janet"); +// l.add("Marco"); +// l.add("Ralph"); + +// for(Enumeration e=l.elements(); e.hasMoreElements();) { +// System.out.println(e.nextElement()); +// } + +// System.out.println(l + ".contains(\"Bela\"): " + l.contains("Bela")); + + +// l.add(new Integer(1)); +// l.add(new Integer(2)); +// l.add(new Integer(5)); +// l.add(new Integer(6)); + + +// System.out.println(l + ".contains(2): " + l.contains(new Integer(2))); +// } + + + + + + + public static void main(String[] args) { + List l=new List(); + Long n; + + + l.addAtHead(new Integer(1)); + l.addAtHead(new Integer(2)); + l.addAtHead(new Integer(3)); + l.addAtHead(new Integer(4)); + l.addAtHead(new Integer(5)); + + System.out.println("list: " + l.toStringWithDelimiter(", ")); + + System.out.println("Removed from head: " + l.removeFromHead()); + System.out.println("Removed from head: " + l.removeFromHead()); + System.out.println("Removed from head: " + l.removeFromHead()); + System.out.println("Removed from head: " + l.removeFromHead()); + System.out.println("Removed from head: " + l.removeFromHead()); + System.out.println("Removed from head: " + l.removeFromHead()); + System.out.println("Removed from head: " + l.removeFromHead()); + + + System.out.print("Adding 50000 numbers:"); + for(long i=0; i < 50000; i++) { + n=new Long(i); + if(i % 2 == 0) { + l.addAtHead(n); + } + else { + l.add(n); + } + } + System.out.println(" OK"); + + long num=0; + System.out.print("Removing all elements: "); + while((l.remove()) != null) + num++; + System.out.println("OK, removed " + num + " objects"); + } + + + + + +}