[Red5devs] [red5 commit] r3105 - in java/server/branches/paulg_mp4/src/org/red5: logging server/net/rtmps server/util

codesite-noreply at google.com codesite-noreply at google.com
Thu Sep 25 14:06:32 PDT 2008


Author: mondain
Date: Thu Sep 25 14:05:37 2008
New Revision: 3105

Added:
    java/server/branches/paulg_mp4/src/org/red5/logging/
     
java/server/branches/paulg_mp4/src/org/red5/logging/ContextLoggingListener.java
     
java/server/branches/paulg_mp4/src/org/red5/logging/LoggerContextFilter.java
     
java/server/branches/paulg_mp4/src/org/red5/logging/LoggingContextSelector.java
    java/server/branches/paulg_mp4/src/org/red5/logging/W3CAppender.java
    java/server/branches/paulg_mp4/src/org/red5/server/net/rtmps/
     
java/server/branches/paulg_mp4/src/org/red5/server/net/rtmps/TomcatRTMPSLoader.java
    java/server/branches/paulg_mp4/src/org/red5/server/util/
    java/server/branches/paulg_mp4/src/org/red5/server/util/FileUtil.java
     
java/server/branches/paulg_mp4/src/org/red5/server/util/PropertyConverter.java

Log:
added "missing" files

Added:  
java/server/branches/paulg_mp4/src/org/red5/logging/ContextLoggingListener.java
==============================================================================
--- (empty file)
+++  
java/server/branches/paulg_mp4/src/org/red5/logging/ContextLoggingListener.java	 
Thu Sep 25 14:05:37 2008
@@ -0,0 +1,87 @@
+package org.red5.logging;
+
+import java.util.List;
+
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+import org.slf4j.Logger;
+import org.slf4j.impl.StaticLoggerBinder;
+
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.selector.ContextSelector;
+
+/**
+ * A servlet context listener that puts this contexts LoggerContext
+ * into a static map of logger contexts within an overall singleton
+ * log context selector.
+ *
+ * To use it, add the following line to a web.xml file
+ *<pre>
+	&lt;listener&gt;
+		 
&lt;listener-class&gt;org.red5.logging.ContextLoggingListener&lt;/listener-class&gt;
+	&lt;/listener&gt;
+ *</pre>
+ *
+ * @author Paul Gregoire (mondain at gmail.com)
+ */
+public class ContextLoggingListener implements ServletContextListener {
+
+	public void contextDestroyed(ServletContextEvent event) {
+		System.out.println("Context destroying...");
+
+		String contextName = pathToName(event);
+		System.out.printf("About to detach context named %s\n", contextName);
+
+		ContextSelector selector =  
StaticLoggerBinder.SINGLETON.getContextSelector();
+		LoggerContext context = selector.detachLoggerContext(contextName);
+		if (context != null) {
+			Logger logger = context.getLogger(LoggerContext.ROOT_NAME);
+			logger.info("Shutting down context {}", contextName);
+			context.shutdownAndReset();
+		} else {
+			System.err.printf("No context named %s was found", contextName);
+		}
+	}
+
+	public void contextInitialized(ServletContextEvent event) {
+		System.out.println("Context init...");
+
+		String contextName = pathToName(event);
+		System.out.printf("Logger name for context: %s\n", contextName);
+
+		try {
+			LoggingContextSelector selector = (LoggingContextSelector)  
StaticLoggerBinder.SINGLETON.getContextSelector();
+		
+			selector.setContextName(contextName);
+			LoggerContext context = selector.getLoggerContext();
+
+			if (context != null) {
+				Logger logger = context.getLogger(LoggerContext.ROOT_NAME);
+				logger.info("Starting up context {}", contextName);
+			} else {
+				System.err.printf("No context named %s was found", contextName);
+			}
+			
+			List<String> ctxNameList = selector.getContextNames();
+			for (String s : ctxNameList) {
+				System.out.printf("Selector context name: %s\n", s);
+			}			
+			
+		} catch (Exception e) {
+			System.err.println("LoggingContextSelector is not the correct type");
+			e.printStackTrace();
+		}
+
+	}
+
+	private String pathToName(ServletContextEvent event) {
+		String contextName = event.getServletContext().getContextPath()
+				.replaceAll("/", "");
+		if (contextName.equals("")) {
+			contextName = "root";
+		}
+		return contextName;
+	}
+
+}

Added:  
java/server/branches/paulg_mp4/src/org/red5/logging/LoggerContextFilter.java
==============================================================================
--- (empty file)
+++  
java/server/branches/paulg_mp4/src/org/red5/logging/LoggerContextFilter.java	 
Thu Sep 25 14:05:37 2008
@@ -0,0 +1,79 @@
+package org.red5.logging;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+import org.slf4j.LoggerFactory;
+import org.slf4j.impl.StaticLoggerBinder;
+
+import ch.qos.logback.classic.LoggerContext;
+
+/**
+ * A servlet filter that puts this contexts LoggerContext into a  
Threadlocal variable.
+ *
+ * It removes it after the request is processed.
+ *
+ * To use it, add the following lines to a web.xml file
+ *<pre>
+	&lt;filter&gt;
+		&lt;filter-name&gt;LoggerContextFilter&lt;/filter-name&gt;
+		 
&lt;filter-class&gt;org.red5.logging.LoggerContextFilter&lt;/filter-class&gt;
+	&lt;/filter&gt;
+	&lt;filter-mapping&gt;
+		&lt;filter-name&gt;LoggerContextFilter&lt;/filter-name&gt;
+		&lt;url-pattern&gt;/*&lt;/url-pattern&gt;
+	&lt;/filter-mapping&gt;
+ *</pre>
+ *
+ * @author Paul Gregoire (mondain at gmail.com)
+ */
+public class LoggerContextFilter implements Filter {
+	
+	private String contextName;
+	
+	public void destroy() {
+	}
+
+	public void doFilter(ServletRequest request, ServletResponse response,
+			FilterChain chain) throws IOException, ServletException {
+		
+		System.out.printf("Context name: %s\n", contextName);
+		
+		LoggingContextSelector selector = (LoggingContextSelector)  
StaticLoggerBinder.SINGLETON.getContextSelector();
+		System.out.println("Context select type: " +  
selector.getClass().getName());
+		
+		LoggerContext ctx = selector.getLoggerContext(contextName);
+
+		//load default logger context if its null
+		if (ctx == null) {
+			System.out.println("Logger context was null, getting default");
+			ctx = (LoggerContext) LoggerFactory.getILoggerFactory();
+		}
+		
+		//evaluate context name against logger context name
+		if (!contextName.equals(ctx.getName())) {
+			System.err.printf("Logger context name and context name dont match  
(%s != %s)\n", contextName, ctx.getName());
+		}
+		
+		if (ctx != null) {
+			System.out.printf("Logger context name: %s\n", ctx.getName());
+			selector.setLocalContext(ctx);
+		}
+
+		try {
+			chain.doFilter(request, response);
+		} finally {
+			selector.removeLocalContext();	
+		}
+	}
+
+	public void init(FilterConfig config) throws ServletException {
+		contextName =  
config.getServletContext().getContextPath().replaceAll("/", "");
+	}
+}

Added:  
java/server/branches/paulg_mp4/src/org/red5/logging/LoggingContextSelector.java
==============================================================================
--- (empty file)
+++  
java/server/branches/paulg_mp4/src/org/red5/logging/LoggingContextSelector.java	 
Thu Sep 25 14:05:37 2008
@@ -0,0 +1,159 @@
+package org.red5.logging;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.joran.JoranConfigurator;
+import ch.qos.logback.classic.selector.ContextSelector;
+import ch.qos.logback.classic.util.ContextInitializer;
+import ch.qos.logback.core.joran.spi.JoranException;
+import ch.qos.logback.core.util.Loader;
+import ch.qos.logback.core.util.StatusPrinter;
+
+/**
+ * A class that allows the LoggerFactory to access an web context based  
LoggerContext.
+ *
+ * Add this java option  
-Dlogback.ContextSelector=org.red5.logging.LoggingContextSelector
+ *
+ * @author Paul Gregoire (mondain at gmail.com)
+ */
+public class LoggingContextSelector implements ContextSelector {
+
+	private static final ConcurrentMap<String, LoggerContext> contextMap =  
new ConcurrentHashMap<String, LoggerContext>();
+
+	private final ThreadLocal<LoggerContext> threadLocal = new  
ThreadLocal<LoggerContext>();
+
+	private final LoggerContext defaultContext;
+	
+	private String contextName;
+
+	private String contextConfigFile;
+
+	public LoggingContextSelector(LoggerContext context) {
+		System.out.printf("Setting default logging context: %s\n",  
context.getName());
+		defaultContext = context;
+	}
+
+	public LoggerContext getLoggerContext() {
+		System.out.println("getLoggerContext request");		
+		// First check if ThreadLocal has been set already
+		LoggerContext lc = threadLocal.get();
+		if (lc != null) {
+			System.out.printf("Thread local found: %s\n", lc.getName());
+			return lc;
+		}
+
+		if (contextName == null) {
+			System.out.println("Context name was null, returning default");
+			// We return the default context
+			return defaultContext;
+		} else {
+			// Let's see if we already know such a context
+			LoggerContext loggerContext = contextMap.get(contextName);
+			System.out.printf("Logger context for %s is %s\n", contextName,  
loggerContext);
+
+			if (loggerContext == null) {
+				// We have to create a new LoggerContext
+				loggerContext = new LoggerContext();
+				loggerContext.setName(contextName);
+
+				if (contextConfigFile == null) {
+					contextConfigFile = String.format("logback-%s.xml", contextName);
+					System.out.printf("Context logger config file: %s\n",  
contextConfigFile);
+				}
+				
+				ClassLoader classloader =  
Thread.currentThread().getContextClassLoader();
+				System.out.printf("Thread context cl: %s\n", classloader);
+				ClassLoader classloader2 = Loader.class.getClassLoader();
+				System.out.printf("Loader tcl: %s\n", classloader2);
+				
+				//URL url = Loader.getResourceBySelfClassLoader(contextConfigFile);
+				URL url = Loader.getResource(contextConfigFile, classloader);
+				if (url != null) {
+					try {
+						JoranConfigurator configurator = new JoranConfigurator();
+						loggerContext.shutdownAndReset();
+						configurator.setContext(loggerContext);
+						configurator.doConfigure(url);
+					} catch (JoranException e) {
+						StatusPrinter.print(loggerContext);
+					}
+				} else {
+					try {
+						ContextInitializer ctxInit = new ContextInitializer(loggerContext);
+						ctxInit.autoConfig();
+					} catch (JoranException je) {
+						StatusPrinter.print(loggerContext);
+					}
+				}
+
+				System.out.printf("Adding logger context: %s to map for  
context: %s\n", loggerContext.getName(), contextName);
+				contextMap.put(contextName, loggerContext);
+			}
+			return loggerContext;
+		}
+	}
+
+	public LoggerContext getLoggerContext(String name) {
+		System.out.printf("getLoggerContext request for %s\n", name);
+		System.out.printf("Context is in map: %s\n",  
contextMap.containsKey(name));
+		return contextMap.get(name);
+	}	
+	
+	public LoggerContext getDefaultLoggerContext() {
+		return defaultContext;
+	}
+
+	public void attachLoggerContext(String contextName,
+			LoggerContext loggerContext) {
+		contextMap.put(contextName, loggerContext);
+	}
+
+	public LoggerContext detachLoggerContext(String loggerContextName) {
+		return contextMap.remove(loggerContextName);
+	}
+
+	public List<String> getContextNames() {
+		List<String> list = new ArrayList<String>();
+		list.addAll(contextMap.keySet());
+		return list;
+	}
+
+	public void setContextName(String contextName) {
+		this.contextName = contextName;
+	}
+
+	public void setContextConfigFile(String contextConfigFile) {
+		this.contextConfigFile = contextConfigFile;
+	}
+
+	/**
+	 * Returns the number of managed contexts Used for testing purposes
+	 *
+	 * @return the number of managed contexts
+	 */
+	public int getCount() {
+		return contextMap.size();
+	}
+
+	/**
+	 * These methods are used by the LoggerContextFilter.
+	 *
+	 * They provide a way to tell the selector which context to use, thus  
saving
+	 * the cost of a JNDI call at each new request.
+	 *
+	 * @param context
+	 */
+	public void setLocalContext(LoggerContext context) {
+		threadLocal.set(context);
+	}
+
+	public void removeLocalContext() {
+		threadLocal.remove();
+	}
+
+}

Added: java/server/branches/paulg_mp4/src/org/red5/logging/W3CAppender.java
==============================================================================
--- (empty file)
+++ java/server/branches/paulg_mp4/src/org/red5/logging/W3CAppender.java	 
Thu Sep 25 14:05:37 2008
@@ -0,0 +1,276 @@
+package org.red5.logging;
+
+/*
+ * RED5 Open Source Flash Server - http://www.osflash.org/red5
+ *
+ * Copyright (c) 2006-2008 by respective authors (see below). All rights  
reserved.
+ *
+ * This library is free software; you can redistribute it and/or modify it  
under the
+ * terms of the GNU Lesser General Public License as published by the Free  
Software
+ * Foundation; either version 2.1 of the License, or (at your option) any  
later
+ * version.
+ *
+ * This library is distributed in the hope that it will be useful, but  
WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or  
FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more  
details.
+ *
+ * You should have received a copy of the GNU Lesser General Public  
License along
+ * with this library; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.red5.server.api.Red5;
+
+import ch.qos.logback.classic.spi.LoggingEvent;
+import ch.qos.logback.core.FileAppender;
+import ch.qos.logback.core.status.ErrorStatus;
+
+/**
+ * Logback appender for the Extended W3C format.
+ *
+ * @see http://www.w3.org/TR/WD-logfile.html
+ * @author Paul Gregoire (mondain at gmail.com)
+ */
+public class W3CAppender extends FileAppender<LoggingEvent> {
+	
+	/**
+	#Software: Red5 0.7.1
+	#Version: 1.0
+	#Date: 1998-11-19 22:48:39
+	#Fields: date time c-ip cs-username s-ip cs-method
+	*/
+
+	//whether or not the header has been written
+	private static boolean headerWritten;
+
+	//events that are to be logged
+	private static String events;
+
+	//linked list to preserve order
+	private static List<String> eventsList = new ArrayList<String>();	
+	
+	//fields that are to be logged
+	private static String fields;
+	
+	//linked list to preserve order
+	private static LinkedList<String> fieldList = new LinkedList<String>();
+	
+	public W3CAppender() {
+	}
+
+	public void setEvents(String events) {
+		W3CAppender.events = events;
+		//make a list out of the event names
+		String[] arr = events.split(";");
+		for (String s : arr) {
+			eventsList.add(s);
+		}		
+	}
+
+	public String getEvents() {
+		return events;
+	}
+
+	public void setFields(String fields) {	
+		W3CAppender.fields = fields;
+		//make a list out of the field names
+		String[] arr = fields.split(";");
+		for (String s : arr) {
+			fieldList.add(s);
+		}
+	}
+
+	public String getFields() {	
+		return fields;
+	}
+
+	@Override
+	public synchronized void doAppend(LoggingEvent event) {
+		
+		//get the log message
+		String message = event.getFormattedMessage();
+		
+		//look for w3c prefix
+		if (!message.startsWith("W3C")) {
+			return;
+		}
+		
+		//  
http://logback.qos.ch/apidocs/ch/qos/logback/classic/spi/LoggingEvent.html
+		StringBuilder sbuf = new StringBuilder(128);
+
+		//see if header has been written
+		if (!headerWritten) {
+			//build the header
+			StringBuilder sb = new StringBuilder("#Software: ");
+			sb.append(Red5.VERSION);
+			sb.append("\n#Version: 1.0");
+			sb.append("\n#Date: ");
+			sb.append(new Date());
+			sb.append("\n#Fields: ");
+			for (String field : fields.split(";")) {
+				sb.append(field);
+				sb.append(' ');
+			}
+			sb.append("\n");
+			//String header = sb.toString();
+			//System.out.print(header);
+			sbuf.append(sb.toString());
+			headerWritten = true;
+			sb = null;
+		}
+
+		//break the message into pieces
+		String[] arr = message.split(" ");
+		//create a map
+		Map<String, String> elements = new HashMap<String, String>(arr.length);
+		int i = 0;
+		for (String s : arr) {
+			if ((i = s.indexOf(':')) != -1) {
+				String key = s.substring(0, i);
+				String value = s.substring(i + 1);
+				//System.out.println("Key: " + key + " Value: " + value);
+				elements.put(key, value);
+			}
+		}
+		
+		//Events					Categories
+        //connect-pending			session
+        //connect					session
+        //disconnect                session
+        //publish                   stream
+        //unpublish                 stream
+        //play                      stream
+        //pause                     stream
+        //unpause                   stream
+        //seek                      stream
+        //stop                      stream
+        //record                    stream
+        //recordstop                stream
+        //server-start              server
+        //server-stop               server
+        //vhost-start               vhost
+        //vhost-stop                vhost
+        //app-start                 application
+        //app-stop                  application
+		
+		//filter based on event type - asterik allows all events
+		if (!events.equals("*")) {
+    		if (!eventsList.contains(elements.get("x-event"))) {
+    			//System.err.println("Filtered out -  
event: "+elements.get("x-event")+" event list: "+eventsList);
+    			elements.clear();
+    			elements = null;
+    			sbuf = null;
+    			return;
+    		}
+		}
+		
+		//x-category		event category		
+		//x-event			type of event
+		//date				date at which the event occurred
+		//time				time at which the event occurred
+		//tz               	time zone information
+		//x-ctx            	event dependant context information
+		//s-ip		        ip address[es] of the server
+		//x-pid            	server process id
+		//x-cpu-load       	cpu load
+		//x-mem-load       	memory load (as reported in getServerStats)
+		//x-adaptor        	adaptor name
+		//x-vhost          	vhost name
+		//x-app	          	application name
+		//x-appinst        	application instance name
+		//x-duration	    duration of an event/session
+		//x-status		    status code					
+		//c-ip             	client ip address
+		//c-proto          	connection protocol - rtmp or rtmpt
+		//s-uri            	uri of the fms application
+		//cs-uri-stem      	stem of s-uri
+		//cs-uri-query     	query portion of s-uri
+		//c-referrer       	uri of the referrer
+		//c-user-agent     	user agent
+		//c-client-id      	client id
+		//cs-bytes         	bytes transferred from client to server
+		//sc-bytes         	bytes transferred from server to client
+		//c-connect-type   	type of connection received by the server
+		//x-sname          	stream name
+		//x-sname-query    	query portion of stream uri
+		//x-suri-query		same as x-sname-query						               	
+		//x-suri-stem		cs-uri-stem + x-sname + x-file-ext			       	
+		//x-suri			x-suri-stem + x-suri-query						
+		//x-file-name      	full file path of recorded stream
+		//x-file-ext       	stream type (flv or mp3)
+		//x-file-size      	stream size in bytes
+		//x-file-length    	stream length in seconds
+		//x-spos           	stream position
+		//cs-stream-bytes  	stream bytes transferred from client to server
+		//sc-stream-bytes  	stream bytes transferred from server to client
+		//x-service-name   	name of the service providing the connection
+		//x-sc-qos-bytes	bytes transferred from server to client for quality of  
service	
+		//x-comment	      	comments		
+		
+		//we may need date and/or time
+		Calendar cal = GregorianCalendar.getInstance();
+		cal.clear();
+		cal.setTimeInMillis(event.getTimeStamp());
+		
+		//loop through the field names and grab the values from the map
+		//fields without a value get a tab character as a place holder if
+		//their value is not available to the server
+		for (String field : fieldList) {
+			String value = elements.get(field);
+			if (value == null) {
+				if ("date".equals(field)) {
+					sbuf.append(cal.get(Calendar.MONTH) + 1);
+					sbuf.append('/');
+					sbuf.append(cal.get(Calendar.DAY_OF_MONTH));
+					sbuf.append('/');
+					sbuf.append(cal.get(Calendar.YEAR));
+				} else if ("time".equals(field)) {
+					sbuf.append(cal.get(Calendar.HOUR_OF_DAY));
+					sbuf.append(':');
+					int min = cal.get(Calendar.MINUTE);
+					if (min < 10) {
+						sbuf.append('0');
+						sbuf.append(min);					
+					} else {
+						sbuf.append(min);					
+					}
+				} else if ("s-ip".equals(field)) {		
+					//where should we grab the server ip from?
+					sbuf.append("127.0.0.1");
+				} else if ("x-pid".equals(field)) {		
+					//should we pass thread name?
+					sbuf.append(event.getThreadName());
+				} else {			
+					sbuf.append('\t');
+				}
+			} else {
+				sbuf.append(value);
+			}	
+			//space padded
+			sbuf.append(' ');
+		}
+		
+		sbuf.append("\n");
+		//System.out.println(sbuf.toString());
+		
+		try {
+			this.writer.write(sbuf.toString());
+			if (this.immediateFlush) {
+				this.writer.flush();
+			}
+		} catch (IOException ioe) {
+			addStatus(new ErrorStatus("IO failure in appender", this, ioe));
+		}
+	}
+
+}

Added:  
java/server/branches/paulg_mp4/src/org/red5/server/net/rtmps/TomcatRTMPSLoader.java
==============================================================================
--- (empty file)
+++  
java/server/branches/paulg_mp4/src/org/red5/server/net/rtmps/TomcatRTMPSLoader.java	 
Thu Sep 25 14:05:37 2008
@@ -0,0 +1,162 @@
+package org.red5.server.net.rtmps;
+
+/*
+ * RED5 Open Source Flash Server - http://www.osflash.org/red5
+ *
+ * Copyright (c) 2006-2008 by respective authors (see below). All rights  
reserved.
+ *
+ * This library is free software; you can redistribute it and/or modify it  
under the
+ * terms of the GNU Lesser General Public License as published by the Free  
Software
+ * Foundation; either version 2.1 of the License, or (at your option) any  
later
+ * version.
+ *
+ * This library is distributed in the hope that it will be useful, but  
WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or  
FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more  
details.
+ *
+ * You should have received a copy of the GNU Lesser General Public  
License along
+ * with this library; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+import java.io.File;
+import org.apache.catalina.Context;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Loader;
+import org.apache.catalina.Server;
+import org.apache.catalina.Valve;
+import org.apache.catalina.core.AprLifecycleListener;
+import org.apache.catalina.core.StandardHost;
+import org.apache.catalina.core.StandardWrapper;
+import org.apache.catalina.loader.WebappLoader;
+import org.red5.server.api.IServer;
+import org.red5.server.net.rtmpt.TomcatRTMPTLoader;
+import org.red5.server.util.FileUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Loader for the RTMPS server which uses Tomcat.
+ *
+ * @author The Red5 Project (red5 at osflash.org)
+ * @author Paul Gregoire (mondain at gmail.com)
+ */
+public class TomcatRTMPSLoader extends TomcatRTMPTLoader {
+
+	// Initialize Logging
+	private static Logger log =  
LoggerFactory.getLogger(TomcatRTMPSLoader.class);
+	
+	/**
+	 * RTMPS Tomcat engine.
+	 */
+	protected Engine rtmpsEngine;	
+	
+	/**
+	 * Setter for server
+	 *
+	 * @param server
+	 *            Value to set for property 'server'.
+	 */
+	public void setServer(IServer server) {
+		log.debug("RTMPS setServer");
+		this.server = server;
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public void init() {
+		log.info("Loading RTMPS context");
+		
+		ClassLoader classloader = Thread.currentThread().getContextClassLoader();
+
+		rtmpsEngine = embedded.createEngine();
+		rtmpsEngine.setDefaultHost(host.getName());
+		rtmpsEngine.setName("red5RTMPSEngine");
+		rtmpsEngine.setParentClassLoader(classloader);
+		
+		host.setParentClassLoader(classloader);		
+
+		// add the valves to the host
+		for (Valve valve : valves) {
+			log.debug("Adding host valve: {}", valve);
+			((StandardHost) host).addValve(valve);
+		}				
+		
+		// create and add root context
+		File appDirBase = new File(webappFolder);
+		String webappContextDir =  
FileUtil.formatPath(appDirBase.getAbsolutePath(), "/root");
+		Context ctx = embedded.createContext("/", webappContextDir);
+		ctx.setReloadable(false);
+		log.debug("Context name: {}", ctx.getName());
+		Object ldr = ctx.getLoader();
+		if (ldr != null) {
+			if (ldr instanceof WebappLoader) {
+				log.debug("Replacing context loader");				
+				((WebappLoader)  
ldr).setLoaderClass("org.red5.server.tomcat.WebappClassLoader");
+			} else {
+				log.debug("Context loader was instance of {}",  
ldr.getClass().getName());
+			}
+		} else {
+			log.debug("Context loader was null");
+			WebappLoader wldr = new WebappLoader(classloader);
+			wldr.setLoaderClass("org.red5.server.tomcat.WebappClassLoader");
+			ctx.setLoader(wldr);
+		}
+		appDirBase = null;
+		webappContextDir = null;
+		
+		host.addChild(ctx);
+		
+		// add servlet wrapper
+		StandardWrapper wrapper = new StandardWrapper();
+		wrapper.setServletName("RTMPTServlet");
+		wrapper.setServletClass("org.red5.server.net.servlet.RTMPTServlet");
+		ctx.addChild(wrapper);
+		
+		// add servlet mappings
+		ctx.addServletMapping("/open/*", "RTMPTServlet");
+		ctx.addServletMapping("/close/*", "RTMPTServlet");
+		ctx.addServletMapping("/send/*", "RTMPTServlet");
+		ctx.addServletMapping("/idle/*", "RTMPTServlet");
+				
+		// add the host
+		rtmpsEngine.addChild(host);
+
+		// add new Engine to set of Engine for embedded server
+		embedded.addEngine(rtmpsEngine);
+
+		//turn off native apr support
+		AprLifecycleListener listener = new AprLifecycleListener();
+		listener.setSSLEngine("off");
+		connector.addLifecycleListener(listener);
+		
+		log.debug("Protocol handler: {}",  
connector.getProtocolHandlerClassName());
+
+		// set connection properties
+		connector.setSecure(true);
+        connector.setScheme("https");
+
+		// set other connection / protocol handler properties
+		for (String key : connectionProperties.keySet()) {
+			log.debug("Setting connection property: {} = {}", key,  
connectionProperties.get(key));
+			connector.setProperty(key, connectionProperties.get(key));
+		}		
+		
+		// add new Connector to set of Connectors for embedded server,
+		// associated with Engine
+		embedded.addConnector(connector);
+
+		// start server
+		try {
+			log.info("Starting RTMPS engine");
+			//embedded.start();
+			connector.start();
+		} catch (Exception e) {
+			log.error("Error loading tomcat", e);
+		} finally {
+			registerJMX();		
+		}
+
+	}
+	
+}

Added: java/server/branches/paulg_mp4/src/org/red5/server/util/FileUtil.java
==============================================================================
--- (empty file)
+++ java/server/branches/paulg_mp4/src/org/red5/server/util/FileUtil.java	 
Thu Sep 25 14:05:37 2008
@@ -0,0 +1,504 @@
+package org.red5.server.util;
+
+/*
+ * RED5 Open Source Flash Server - http://www.osflash.org/red5
+ *
+ * Copyright (c) 2006-2008 by respective authors (see below). All rights  
reserved.
+ *
+ * This library is free software; you can redistribute it and/or modify it  
under the
+ * terms of the GNU Lesser General Public License as published by the Free  
Software
+ * Foundation; either version 2.1 of the License, or (at your option) any  
later
+ * version.
+ *
+ * This library is distributed in the hope that it will be useful, but  
WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or  
FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more  
details.
+ *
+ * You should have received a copy of the GNU Lesser General Public  
License along
+ * with this library; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+import java.util.Enumeration;
+import java.util.Random;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Generic file utility containing useful file or directory
+ * manipulation functions.
+ *
+ * @author Paul Gregoire (mondain at gmail.com)
+ */
+public class FileUtil {
+
+	private static Logger log = LoggerFactory.getLogger(FileUtil.class);
+	
+	// Random number generator used by name generation
+	private static Random random;
+
+	public static void copyFile(File source, File dest) throws IOException {
+		log.debug("Copy from {} to {}", source.getAbsoluteFile(), dest
+				.getAbsoluteFile());
+		FileInputStream fi = new FileInputStream(source);
+		FileChannel fic = fi.getChannel();
+		MappedByteBuffer mbuf = fic.map(FileChannel.MapMode.READ_ONLY, 0,
+				source.length());
+		fic.close();
+		fi.close();
+		fi = null;
+
+		// ensure the destination directory exists
+		if (!dest.exists()) {
+			String destPath = dest.getPath();
+			log.debug("Destination path: {}", destPath);
+			String destDir = destPath.substring(0, destPath
+					.lastIndexOf(File.separatorChar));
+			log.debug("Destination dir: {}", destDir);
+			File dir = new File(destDir);
+			if (!dir.exists()) {
+				if (dir.mkdirs()) {
+					log.debug("Directory created");
+				} else {
+					log.warn("Directory not created");
+				}
+			}
+			dir = null;
+		}
+
+		FileOutputStream fo = new FileOutputStream(dest);
+		FileChannel foc = fo.getChannel();
+		foc.write(mbuf);
+		foc.close();
+		fo.close();
+		fo = null;
+
+		mbuf.clear();
+		mbuf = null;
+	}
+
+	public static void copyFile(String source, String dest) throws  
IOException {
+		copyFile(new File(source), new File(dest));
+	}
+
+	public static void moveFile(String source, String dest) throws  
IOException {
+		copyFile(source, dest);
+		File src = new File(source);
+		if (src.exists() && src.canRead()) {
+			if (src.delete()) {
+				log.debug("Source file was deleted");
+			} else {
+				log
+						.debug("Source file was not deleted, the file will be deleted on  
exit");
+				src.deleteOnExit();
+			}
+		} else {
+			log.warn("Source file could not be accessed for removal");
+		}
+		src = null;
+	}
+
+	/**
+	 * Deletes a directory and its contents. This will fail if there are any
+	 * file locks or if the directory cannot be emptied.
+	 *
+	 * @param directory
+	 * @throws IOException
+	 */
+	public static boolean deleteDirectory(String directory) throws  
IOException {
+		return deleteDirectory(directory, false);
+	}
+
+	/**
+	 * Deletes a directory and its contents. This will fail if there are any
+	 * file locks or if the directory cannot be emptied.
+	 *
+	 * @param directory
+	 * @param useOSNativeDelete
+	 *            flag to signify use of operating system delete function
+	 * @throws IOException
+	 */
+	public static boolean deleteDirectory(String directory,
+			boolean useOSNativeDelete) throws IOException {
+		boolean result = false;
+		if (!useOSNativeDelete) {
+			File dir = new File(directory);
+			// first all files have to be cleared out
+			for (File file : dir.listFiles()) {
+				if (file.delete()) {
+					log.debug("{} was deleted", file.getName());
+				} else {
+					log.debug("{} was not deleted", file.getName());
+					file.deleteOnExit();
+				}
+				file = null;
+			}
+			// not you may remove the dir
+			if (dir.delete()) {
+				log.debug("Directory was deleted");
+				result = true;
+			} else {
+				log.debug("Directory was not deleted, it may be deleted on exit");
+				dir.deleteOnExit();
+			}
+			dir = null;			
+		} else {
+			Process p = null;
+			Thread std = null;
+			try {
+				Runtime runTime = Runtime.getRuntime();
+				log.debug("Execute runtime");
+				//determine file system type
+				if (File.separatorChar == '\\') {
+					//we are windows
+					p = runTime.exec("CMD /D /C \"RMDIR /Q /S "
+						+ directory.replace('/', '\\') + "\"");
+				} else {
+					//we are unix variant
+					p = runTime.exec("rm -rf " + directory.replace('\\',  
File.separatorChar));
+				}
+				// observe std out
+				std = stdOut(p);
+				// wait for the observer threads to finish
+				while (std.isAlive()) {
+					try {
+						Thread.sleep(250);
+					} catch (Exception e) {
+					}
+				}
+				log.debug("Process threads wait exited");
+				result = true;
+			} catch (Exception e) {
+				log.error("Error running delete script", e);
+			} finally {
+				if (null != p) {
+					log.debug("Destroying process");
+					p.destroy();
+					p = null;
+				}
+				std = null;
+			}
+		}
+		return result;
+	}
+
+	/**
+	 * Rename a file natively; using REN on Windows and mv on *nix.
+	 *
+	 * @param from
+	 * @param to
+	 */
+	public static void rename(String from, String to) {
+		Process p = null;
+		Thread std = null;
+		try {
+			Runtime runTime = Runtime.getRuntime();
+			log.debug("Execute runtime");
+			//determine file system type
+			if (File.separatorChar == '\\') {
+				//we are windows
+				p = runTime.exec("CMD /D /C \"REN " + from + ' ' + to + "\"");
+			} else {
+				//we are unix variant
+				p = runTime.exec("mv -f " + from + ' ' + to);
+			}
+			// observe std out
+			std = stdOut(p);
+			// wait for the observer threads to finish
+			while (std.isAlive()) {
+				try {
+					Thread.sleep(250);
+				} catch (Exception e) {
+				}
+			}
+			log.debug("Process threads wait exited");
+		} catch (Exception e) {
+			log.error("Error running delete script", e);
+		} finally {
+			if (null != p) {
+				log.debug("Destroying process");
+				p.destroy();
+				p = null;
+				std = null;
+			}
+		}		
+	}	
+	
+	/**
+	 * Special method for capture of StdOut.
+	 *
+	 * @return
+	 */
+	private final static Thread stdOut(final Process p) {
+		final byte[] empty = new byte[128];
+		for (int b = 0; b < empty.length; b++) {
+			empty[b] = (byte) 0;
+		}
+		Thread std = new Thread() {
+			public void run() {
+				StringBuilder sb = new StringBuilder(1024);
+				byte[] buf = new byte[128];
+				BufferedInputStream bis = new BufferedInputStream(p
+						.getInputStream());
+				log.debug("Process output:");
+				try {
+					while (bis.read(buf) != -1) {
+						sb.append(new String(buf).trim());
+						// clear buffer
+						System.arraycopy(empty, 0, buf, 0, buf.length);
+					}
+					log.debug(sb.toString());
+					bis.close();
+				} catch (Exception e) {
+					log.error("{}", e);
+				}
+			}
+		};
+		std.setDaemon(true);
+		std.start();
+		return std;
+	}
+
+	/**
+	 * Create a directory.
+	 *
+	 * @param directory
+	 * @return
+	 * @throws IOException
+	 */
+	public static boolean makeDirectory(String directory) throws IOException {
+		return makeDirectory(directory, false);
+	}
+
+	/**
+	 * Create a directory. The parent directories will be created if
+	 * <i>createParents</i> is passed as true.
+	 *
+	 * @param directory
+	 * @param createParents
+	 * @return
+	 * @throws IOException
+	 */
+	public static boolean makeDirectory(String directory, boolean  
createParents)
+			throws IOException {
+		boolean created = false;
+		File dir = new File(directory);
+		if (createParents) {
+			created = dir.mkdirs();
+			if (created) {
+				log.debug("Directory created: {}", dir.getAbsolutePath());
+			} else {
+				log.debug("Directory was not created: {}", dir
+						.getAbsolutePath());
+			}
+		} else {
+			created = dir.mkdir();
+			if (created) {
+				log.debug("Directory created: {}", dir.getAbsolutePath());
+			} else {
+				log.debug("Directory was not created: {}", dir
+						.getAbsolutePath());
+			}
+		}
+		dir = null;
+		return created;
+	}
+
+	/**
+	 * Unzips a given archive to a specified destination directory.
+	 *
+	 * @param compressedFileName
+	 * @param destinationDir
+	 */
+	public static void unzip(String compressedFileName, String  
destinationDir) {
+		
+		//strip everything except the applications name
+		String dirName = compressedFileName.substring(0,  
compressedFileName.indexOf('-'));
+		//String tmpDir = System.getProperty("java.io.tmpdir");
+		File zipDir = new File(compressedFileName);
+		File parent = zipDir.getParentFile();
+		//File tmpDir = new File(System.getProperty("java.io.tmpdir"), dirName);
+		File tmpDir = new File(destinationDir);
+
+		// make the war directory
+		log.debug("Making directory: {}", tmpDir.mkdirs());
+		
+		try {
+			ZipFile zf = new ZipFile(compressedFileName);
+			Enumeration e = zf.entries();
+			while(e.hasMoreElements()) {
+				ZipEntry ze = (ZipEntry) e.nextElement();
+				log.debug("Unzipping {}", ze.getName());
+				if(ze.isDirectory()) {
+					log.debug("is a directory");
+					File dir = new File(tmpDir + "/" + ze.getName());
+					Boolean tmp = dir.mkdir();
+					log.debug("{}", tmp);
+					continue;
+				}
+				FileOutputStream fout = new FileOutputStream(tmpDir + "/" +  
ze.getName());
+				InputStream in = zf.getInputStream(ze);
+				copy(in, fout);
+				in.close();
+				fout.close();
+			}
+			e = null;
+		} catch (IOException e) {
+			log.debug("Error unzipping", e);
+			e.printStackTrace();
+					}
+				}				
+
+	public static void copy(InputStream in, OutputStream out) throws  
IOException {
+		synchronized(in) {
+			synchronized(out) {
+				byte[] buffer = new byte[256];
+				while (true) {
+					int bytesRead = in.read(buffer);
+					if(bytesRead == -1) break;
+					out.write(buffer, 0, bytesRead);
+				}
+			}
+			}
+		}
+
+	/**
+	 * Unzips a given archive to a specified destination directory.
+	 *
+	 * @param compressedFileName
+	 * @param destinationDir
+	 */
+//	public static void unzip(String compressedFileName, String  
destinationDir) {
+//		log.debug("Unzip - file: {} destination: {}", compressedFileName,  
destinationDir);
+//		try {
+//			final int BUFFER = 2048;
+//			BufferedOutputStream dest = null;
+//			FileInputStream fis = new FileInputStream(compressedFileName);
+//			CheckedInputStream checksum = new CheckedInputStream(fis,
+//					new Adler32());
+//			ZipInputStream zis = new ZipInputStream(new BufferedInputStream(
+//					checksum));
+//			ZipEntry entry;
+//			while ((entry = zis.getNextEntry()) != null) {
+//				log.debug("Extracting: {}", entry);
+//				String name = entry.getName();
+//				int count;
+//				byte data[] = new byte[BUFFER];
+//				// write the files to the disk
+//				File destFile = new File(destinationDir, name);
+//				log.debug("Absolute path: {}", destFile.getAbsolutePath());
+//				//create dirs as needed, look for file extension to determine type
+//				if (entry.isDirectory()) {
+//					log.debug("Entry is detected as a directory");
+//					if (destFile.mkdirs()) {
+//						log.debug("Directory created: {}", destFile.getName());
+//					} else {
+//						log.warn("Directory was not created: {}", destFile.getName());
+//					}
+//					destFile = null;
+//					continue;
+//				}				
+//
+//				FileOutputStream fos = new FileOutputStream(destFile);
+//				dest = new BufferedOutputStream(fos, BUFFER);
+//				while ((count = zis.read(data, 0, BUFFER)) != -1) {
+//					dest.write(data, 0, count);
+//				}
+//				dest.flush();
+//				dest.close();
+//				destFile = null;
+//			}
+//			zis.close();
+//			log.debug("Checksum: {}", checksum.getChecksum().getValue());
+//		} catch (Exception e) {
+//			log.error("Error unzipping {}", compressedFileName, e);
+//			e.printStackTrace();
+//		}
+//
+//	}
+	
+    /**
+     * Quick-n-dirty directory formatting to support launching in windows,  
specifically from ant.
+     */
+    public static String formatPath(String absWebappsPath, String  
contextDirName) {
+        StringBuilder path = new StringBuilder(absWebappsPath.length() +  
contextDirName.length());
+        path.append(absWebappsPath);
+        if (log.isDebugEnabled()) {
+        	log.debug("Path start: {}", path.toString());
+        }
+        int idx = -1;
+        if (File.separatorChar != '/') {
+            while ((idx = path.indexOf(File.separator)) != -1) {
+                path.deleteCharAt(idx);
+                path.insert(idx, '/');
+            }
+        }
+        if (log.isDebugEnabled()) {
+        	log.debug("Path step 1: {}", path.toString());
+        }
+        //remove any './'
+        if ((idx = path.indexOf("./")) != -1) {
+        	path.delete(idx, idx + 2);
+        }
+        if (log.isDebugEnabled()) {
+        	log.debug("Path step 2: {}", path.toString());
+        }
+        //add / to base path if one doesnt exist
+        if (path.charAt(path.length() - 1) != '/') {
+        	path.append('/');
+        }
+        if (log.isDebugEnabled()) {
+        	log.debug("Path step 3: {}", path.toString());
+        }
+        //remove the / from the beginning of the context dir
+        if (contextDirName.charAt(0) == '/' && path.charAt(path.length() -  
1) == '/') {
+            path.append(contextDirName.substring(1));
+        } else {
+            path.append(contextDirName);
+        }
+        if (log.isDebugEnabled()) {
+        	log.debug("Path step 4: {}", path.toString());
+        }
+        return path.toString();
+    }	
+	
+	/**
+	 * Generates a custom name containing numbers and an underscore ex.  
282818_00023.
+	 * The name contains current seconds and a random number component.
+	 *
+	 * @return
+	 */
+	public static String generateCustomName() {
+		if (random == null) {
+			random = new Random();
+		}
+    	StringBuilder sb = new StringBuilder();
+    	sb.append(PropertyConverter.getCurrentTimeSeconds());
+    	sb.append('_');
+    	int i = random.nextInt(99999);
+    	if (i < 10) {
+    		sb.append("0000");
+    	} else if (i < 100) {
+       		sb.append("000");
+    	} else if (i < 1000) {
+       		sb.append("00");
+    	} else if (i < 10000) {
+       		sb.append("0");
+    	}    	
+    	sb.append(i);
+    	return sb.toString();
+    }	
+	
+}

Added:  
java/server/branches/paulg_mp4/src/org/red5/server/util/PropertyConverter.java
==============================================================================
--- (empty file)
+++  
java/server/branches/paulg_mp4/src/org/red5/server/util/PropertyConverter.java	 
Thu Sep 25 14:05:37 2008
@@ -0,0 +1,162 @@
+package org.red5.server.util;
+
+/*
+ * RED5 Open Source Flash Server - http://www.osflash.org/red5
+ *
+ * Copyright (c) 2006-2008 by respective authors (see below). All rights  
reserved.
+ *
+ * This library is free software; you can redistribute it and/or modify it  
under the
+ * terms of the GNU Lesser General Public License as published by the Free  
Software
+ * Foundation; either version 2.1 of the License, or (at your option) any  
later
+ * version.
+ *
+ * This library is distributed in the hope that it will be useful, but  
WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or  
FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more  
details.
+ *
+ * You should have received a copy of the GNU Lesser General Public  
License along
+ * with this library; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+import java.util.Calendar;
+
+import org.apache.commons.lang.StringUtils;
+
+/**
+ * Converter for properties originating from properties files.  
Predetermined
+ * string formats are converted into other usable types such as timestamps.
+ *
+ * @author Paul Gregoire (mondain at gmail.com)
+ */
+public class PropertyConverter {
+
+	/**
+	 * Converts a string denoting an amount of time into milliseconds and adds
+	 * it to the current date. Strings are expected to follow this form where  
#
+	 * equals a digit: #M The following are permitted for denoting time: H =
+	 * hours, M = minutes, S = seconds
+	 *
+	 * @param time
+	 * @return time in milliseconds
+	 */
+	public static long convertStringToFutureTimeMillis(String time) {
+		Calendar exp = Calendar.getInstance();
+		if (time.endsWith("H")) {
+			exp.add(Calendar.HOUR, Integer.valueOf(StringUtils
+					.remove(time, 'H')));
+		} else if (time.endsWith("M")) {
+			exp.add(Calendar.MINUTE, Integer.valueOf(StringUtils.remove(time,
+					'M')));
+		} else if (time.endsWith("S")) {
+			exp.add(Calendar.MILLISECOND, Integer.valueOf(StringUtils.remove(
+					time, 'S')) * 1000);
+		}
+		return exp.getTimeInMillis();
+	}
+
+	/**
+	 * Converts a string denoting an amount of time into seconds. Strings are
+	 * expected to follow this form where # equals a digit: #M The following  
are
+	 * permitted for denoting time: H = hours, M = minutes, S = seconds
+	 *
+	 * @param time
+	 * @return time in seconds
+	 */
+	public static int convertStringToTimeSeconds(String time) {
+		int result = 0;
+		if (time.endsWith("H")) {
+			int hoursToAdd = Integer.valueOf(StringUtils.remove(time, 'H'));
+			result = (60 * 60) * hoursToAdd;
+		} else if (time.endsWith("M")) {
+			int minsToAdd = Integer.valueOf(StringUtils.remove(time, 'M'));
+			result = 60 * minsToAdd;
+		} else if (time.endsWith("S")) {
+			int secsToAdd = Integer.valueOf(StringUtils.remove(time, 'S'));
+			result = secsToAdd;
+		}
+		return result;
+	}
+
+	/**
+	 * Converts a string denoting an amount of time into milliseconds. Strings
+	 * are expected to follow this form where # equals a digit: #M The  
following
+	 * are permitted for denoting time: H = hours, M = minutes, S = seconds
+	 *
+	 * @param time
+	 * @return time in milliseconds
+	 */
+	public static long convertStringToTimeMillis(String time) {
+		long result = 0;
+		if (time.endsWith("H")) {
+			long hoursToAdd = Integer.valueOf(StringUtils.remove(time, 'H'));
+			result = ((1000 * 60) * 60) * hoursToAdd;
+		} else if (time.endsWith("M")) {
+			long minsToAdd = Integer.valueOf(StringUtils.remove(time, 'M'));
+			result = (1000 * 60) * minsToAdd;
+		} else if (time.endsWith("S")) {
+			long secsToAdd = Integer.valueOf(StringUtils.remove(time, 'S'));
+			result = 1000 * secsToAdd;
+		}
+		return result;
+	}
+
+	/**
+	 * Converts a string denoting an amount of bytes into an integer value.
+	 * Strings are expected to follow this form where # equals a digit: #M The
+	 * following are permitted for denoting binary size: K = kilobytes, M =
+	 * megabytes, G = gigabytes
+	 *
+	 * @param memSize
+	 * @return size as an integer
+	 */
+	public static int convertStringToMemorySizeInt(String memSize) {
+		int result = 0;
+		if (memSize.endsWith("K")) {
+			result = Integer.valueOf(StringUtils.remove(memSize, 'K')) * 1000;
+		} else if (memSize.endsWith("M")) {
+			result = Integer.valueOf(StringUtils.remove(memSize, 'M')) * 1000 *  
1000;
+		} else if (memSize.endsWith("G")) {
+			result = Integer.valueOf(StringUtils.remove(memSize, 'G')) * 1000 *  
1000 * 1000;
+		}
+		return result;
+	}
+
+	/**
+	 * Converts a string denoting an amount of bytes into an long value.  
Strings
+	 * are expected to follow this form where # equals a digit: #M The  
following
+	 * are permitted for denoting binary size: K = kilobytes, M = megabytes,  
G =
+	 * gigabytes
+	 *
+	 * @param memSize
+	 * @return size as an long
+	 */
+	public static long convertStringToMemorySizeLong(String memSize) {
+		long result = 0;
+		if (memSize.endsWith("K")) {
+			result = Long.valueOf(StringUtils.remove(memSize, 'K')) * 1000;
+		} else if (memSize.endsWith("M")) {
+			result = Long.valueOf(StringUtils.remove(memSize, 'M')) * 1000 * 1000;
+		} else if (memSize.endsWith("G")) {
+			result = Long.valueOf(StringUtils.remove(memSize, 'G')) * 1000 * 1000 *  
1000;
+		}
+		return result;
+	}
+
+	/**
+	 * Quick time converter to keep our timestamps compatible with PHP's  
time()
+	 * (seconds)
+	 */
+	public static Integer getCurrentTimeSeconds() {
+		return convertMillisToSeconds(System.currentTimeMillis());
+	}
+
+	/**
+	 * Quick time converter to keep our timestamps compatible with PHP's  
time()
+	 * (seconds)
+	 */
+	public static Integer convertMillisToSeconds(Long millis) {
+		return Long.valueOf(millis / 1000).intValue();
+	}
+
+}



More information about the Red5devs mailing list