[Red5commits] [2176] Merged with trunk

sgong luke at codegent.com
Mon Jul 9 00:22:16 EDT 2007


Merged with trunk


Timestamp: 07/07/07 04:24:25 EST (2 days ago) 
Change: 2176 
Author: sgong

Files (see diff or trac for details): 
java/server/branches/clustering/conf/log4j.properties
java/server/branches/clustering/conf/red5.xml
java/server/branches/clustering/conf/war/log4j.properties
java/server/branches/clustering/src/org/red5/server/BasicScope.java
java/server/branches/clustering/src/org/red5/server/ClientList.java
java/server/branches/clustering/src/org/red5/server/ClientRegistry.java
java/server/branches/clustering/src/org/red5/server/JettyLoader.java
java/server/branches/clustering/src/org/red5/server/PersistableAttributeStore.java
java/server/branches/clustering/src/org/red5/server/Scope.java
java/server/branches/clustering/src/org/red5/server/Shutdown.java
java/server/branches/clustering/src/org/red5/server/Standalone.java
java/server/branches/clustering/src/org/red5/server/TomcatLoader.java
java/server/branches/clustering/src/org/red5/server/api/persistence/IPersistenceStore.java
java/server/branches/clustering/src/org/red5/server/api/persistence/PersistenceUtils.java
java/server/branches/clustering/src/org/red5/server/api/stream/IStreamFilenameGenerator.java
java/server/branches/clustering/src/org/red5/server/net/rtmp/RTMPMinaIoHandler.java
java/server/branches/clustering/src/org/red5/server/net/rtmp/codec/RTMPProtocolDecoder.java
java/server/branches/clustering/src/org/red5/server/net/rtmpt/RTMPTHandler.java
java/server/branches/clustering/src/org/red5/server/persistence/FilePersistence.java
java/server/branches/clustering/src/org/red5/server/persistence/FilePersistenceThread.java
java/server/branches/clustering/src/org/red5/server/persistence/RamPersistence.java
java/server/branches/clustering/src/org/red5/server/service/ServiceUtils.java
java/server/branches/clustering/src/org/red5/server/stream/BroadcastScope.java
java/server/branches/clustering/src/org/red5/server/stream/ClientBroadcastStream.java
java/server/branches/clustering/src/org/red5/server/stream/DefaultStreamFilenameGenerator.java
java/server/branches/clustering/src/org/red5/server/stream/ProviderService.java
java/server/branches/clustering/src/org/red5/server/stream/ServerStream.java


Trac: http://mirror1.cvsdude.com/trac/osflash/red5/changeset/2176

Index: /java/server/branches/clustering/conf/war/log4j.properties
===================================================================
--- /java/server/branches/clustering/conf/war/log4j.properties (revision 2034)
+++ /java/server/branches/clustering/conf/war/log4j.properties (revision 2176)
@@ -50,5 +50,5 @@
 log4j.appender.CONSOLE.Threshold=DEBUG
 log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
-log4j.appender.CONSOLE.layout.ConversionPattern=[%p] %r %t:( %c.%M ) %m %n
+log4j.appender.CONSOLE.layout.ConversionPattern=[%p] %d{ISO8601} %t:( %c.%M ) %m %n
 
 log4j.appender.FLOG=org.apache.log4j.RollingFileAppender
@@ -58,5 +58,5 @@
 log4j.appender.FLOG.MaxBackupIndex=1
 log4j.appender.FLOG.layout=org.apache.log4j.PatternLayout
-log4j.appender.FLOG.layout.ConversionPattern=[%p] %r %t:( %c{1}.%M ) %m %n
+log4j.appender.FLOG.layout.ConversionPattern=[%p] %d{ISO8601} %t:( %c{1}.%M ) %m %n
 
 log4j.appender.PROXY=org.apache.log4j.RollingFileAppender
@@ -66,3 +66,3 @@
 log4j.appender.PROXY.MaxBackupIndex=1
 log4j.appender.PROXY.layout=org.apache.log4j.PatternLayout
-log4j.appender.PROXY.layout.ConversionPattern=[%c{1}] (%r) %m %n
+log4j.appender.PROXY.layout.ConversionPattern=[%c{1}] (%d{ISO8601}) %m %n
Index: /java/server/branches/clustering/conf/red5.xml
===================================================================
--- /java/server/branches/clustering/conf/red5.xml (revision 1654)
+++ /java/server/branches/clustering/conf/red5.xml (revision 2176)
@@ -7,4 +7,9 @@
 
 	<!-- This file just wires together the context tree. Its accessed by ContextSingletonBeanFactoryLocator -->
+	
+	<bean id="placeholderConfig"
+		class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
+		<property name="location" value="classpath:/red5.properties" />
+	</bean>
 
 	<!-- First we load the common context, its shared between all the other contexts -->
@@ -33,7 +38,13 @@
 	<!-- Now we can load the servlet engine, this has to happen after the context are loaded -->
 
- 	<bean id="jetty6.server" class="org.red5.server.JettyLoader" init-method="init" autowire="byType" depends-on="context.loader" />
+ 	<bean id="jetty6.server" class="org.red5.server.JettyLoader" init-method="init" autowire="byType" depends-on="context.loader">
+		<property name="webappFolder" value="${red5.root}/webapps" />
+	</bean>
+
 <!--
 	<bean id="tomcat.server" class="org.red5.server.TomcatLoader" init-method="init" destroy-method="shutdown" autowire="byType" depends-on="context.loader">
+		<!- - Note: the webapp root folder must be specified before the "contexts" property - ->
+		<property name="webappFolder" value="${red5.root}/webapps" />
+	
 	    <property name="embedded">
 	    	<bean class="org.apache.catalina.startup.Embedded" />
Index: /java/server/branches/clustering/conf/log4j.properties
===================================================================
--- /java/server/branches/clustering/conf/log4j.properties (revision 1947)
+++ /java/server/branches/clustering/conf/log4j.properties (revision 2176)
@@ -67,5 +67,5 @@
 log4j.appender.CONSOLE.Threshold=DEBUG
 log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
-log4j.appender.CONSOLE.layout.ConversionPattern=[%p] %r %t:( %c.%M ) %m %n
+log4j.appender.CONSOLE.layout.ConversionPattern=[%p] %d{ISO8601} %t:( %c.%M ) %m %n
 
 log4j.appender.FLOG=org.apache.log4j.RollingFileAppender
@@ -75,5 +75,5 @@
 log4j.appender.FLOG.MaxBackupIndex=1
 log4j.appender.FLOG.layout=org.apache.log4j.PatternLayout
-log4j.appender.FLOG.layout.ConversionPattern=[%p] %r %t:( %c{1}.%M ) %m %n
+log4j.appender.FLOG.layout.ConversionPattern=[%p] %d{ISO8601} %t:( %c{1}.%M ) %m %n
 
 log4j.appender.PROXY=org.apache.log4j.RollingFileAppender
@@ -83,3 +83,3 @@
 log4j.appender.PROXY.MaxBackupIndex=1
 log4j.appender.PROXY.layout=org.apache.log4j.PatternLayout
-log4j.appender.PROXY.layout.ConversionPattern=[%c{1}] (%r) %m %n
+log4j.appender.PROXY.layout.ConversionPattern=[%c{1}] (%d{ISO8601}) %m %n
Index: /java/server/branches/clustering/src/org/red5/server/persistence/FilePersistence.java
===================================================================
--- /java/server/branches/clustering/src/org/red5/server/persistence/FilePersistence.java (revision 2076)
+++ /java/server/branches/clustering/src/org/red5/server/persistence/FilePersistence.java (revision 2176)
@@ -67,9 +67,14 @@
 
     /**
-     * Whether there's ned to check for empty directories
+     * Whether there's need to check for empty directories
      */
     // TODO: make this configurable
 	private boolean checkForEmptyDirectories = true;
 
+	/**
+	 * Thread to serialize persistent objects.
+	 */
+	private FilePersistenceThread storeThread = null;
+	
     /**
      * Create file persistence object from given resource pattern resolver
@@ -79,4 +84,5 @@
 		super(resolver);
 		setPath(path);
+		storeThread = FilePersistenceThread.getInstance();
 	}
 
@@ -88,4 +94,5 @@
 		super(scope);
 		setPath(path);
+		storeThread = FilePersistenceThread.getInstance();
 	}
 
@@ -224,5 +231,5 @@
 					// We need to create the object first
 					try {
-						Class theClass = Class.forName(className);
+						Class<?> theClass = Class.forName(className);
 						Constructor constructor = null;
 						try {
@@ -323,5 +330,5 @@
      * @return                 <code>true</code> on success, <code>false</code> otherwise
      */
-    private boolean saveObject(IPersistable object) {
+    protected boolean saveObject(IPersistable object) {
 		String path = getObjectFilepath(object, true);
 		Resource resPath = resources.getResource(path);
@@ -363,5 +370,5 @@
 			return true;
 		} catch (IOException e) {
-			log.error("Could not create / write file " + filename);
+			log.error("Could not create / write file " + filename, e);
 			return false;
 		}
@@ -375,7 +382,7 @@
 		}
 
-		boolean persistent = this.saveObject(object);
-		object.setPersistent(persistent);
-		return persistent;
+		storeThread.modified(object, this);
+		object.setPersistent(true);
+		return true;
 	}
 
@@ -448,3 +455,11 @@
 	}
 
+	/** {@inheritDoc} */
+    @Override
+	public void notifyClose() {
+    	// Write any pending objects
+		storeThread.notifyClose(this);
+		super.notifyClose();
+	}
+
 }
Index: /java/server/branches/clustering/src/org/red5/server/persistence/RamPersistence.java
===================================================================
--- /java/server/branches/clustering/src/org/red5/server/persistence/RamPersistence.java (revision 1935)
+++ /java/server/branches/clustering/src/org/red5/server/persistence/RamPersistence.java (revision 2176)
@@ -174,3 +174,8 @@
 		return objects.values();
 	}
+
+	/** {@inheritDoc} */
+	public void notifyClose() {
+		objects.clear();
+	}
 }
Index: /java/server/branches/clustering/src/org/red5/server/persistence/FilePersistenceThread.java
===================================================================
--- /java/server/branches/clustering/src/org/red5/server/persistence/FilePersistenceThread.java (revision 2176)
+++ /java/server/branches/clustering/src/org/red5/server/persistence/FilePersistenceThread.java (revision 2176)
@@ -0,0 +1,187 @@
+package org.red5.server.persistence;
+
+/*
+ * RED5 Open Source Flash Server - http://www.osflash.org/red5
+ *
+ * Copyright (c) 2006-2007 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.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.red5.server.api.persistence.IPersistable;
+
+/**
+ * Thread that writes modified persistent objects to the filesystem periodically.
+ * 
+ * @author The Red5 Project (red5 at osflash.org)
+ * @author Joachim Bauch (jojo at struktur.de)
+ */
+public class FilePersistenceThread extends Thread {
+
+    /**
+     * Logger
+     */
+    private Log log = LogFactory.getLog(FilePersistenceThread.class.getName());
+    
+	/**
+	 * Interval to serialize modified objects in milliseconds.
+	 *
+	 */
+    // TODO: make this configurable
+	private int storeInterval = 10000;
+	
+	/**
+	 * Modified objects that need to be stored.
+	 */
+	private Map<IPersistable, FilePersistence> modifiedObjects = new HashMap<IPersistable, FilePersistence>();
+	
+	/**
+	 * Modified objects for each store.
+	 */
+	private Map<FilePersistence, Set<IPersistable>> objectStores = new HashMap<FilePersistence, Set<IPersistable>>();
+	
+	/**
+	 * Singleton instance.
+	 */
+	private static volatile FilePersistenceThread instance = null;
+	
+	/**
+	 * Return singleton instance of the thread.
+	 * 
+	 * @return
+	 */
+	public static FilePersistenceThread getInstance() {
+		if (instance == null) {
+			// Only synchronize if thread doesn't exist yet.
+			synchronized (FilePersistenceThread.class) {
+				if (instance == null) {
+					instance = new FilePersistenceThread();
+					instance.start();
+				}
+			}
+		}
+		
+		return instance;
+	}
+	
+	/**
+	 * Create instance of the thread.
+	 */
+	private FilePersistenceThread() {
+		super();
+		setName("FilePersistenceThread");
+	}
+
+	/**
+	 * Notify thread that an object was modified in a persistence store.
+	 * 
+	 * @param object
+	 * @param store
+	 */
+	protected void modified(IPersistable object, FilePersistence store) {
+		FilePersistence previous;
+		synchronized (modifiedObjects) {
+			previous = modifiedObjects.put(object, store);
+			Set<IPersistable> objects = objectStores.get(store);
+			if (objects == null) {
+				objects = new HashSet<IPersistable>();
+				objectStores.put(store, objects);
+			}
+			objects.add(object);
+		}
+		
+		if (previous != null && !previous.equals(store)) {
+			log.warn("Object " + object + " was also modified in " + previous + ", saving instantly");
+			previous.saveObject(object);
+			Set<IPersistable> objects = objectStores.get(previous);
+			if (objects != null) {
+				objects.remove(previous);
+			}
+		}
+	}
+	
+	/**
+	 * Write any pending objects for the given store to disk.
+	 * 
+	 * @param store
+	 */
+	protected void notifyClose(FilePersistence store) {
+		Set<IPersistable> objects;
+		// Get snapshot of currently modified objects.
+		synchronized (modifiedObjects) {
+			objects = objectStores.remove(store);
+			if (objects != null) {
+				for (IPersistable object: objects) {
+					modifiedObjects.remove(object);
+				}
+			}
+		}
+		
+		if (objects == null || objects.isEmpty()) {
+			return;
+		}
+		
+		// Store pending objects
+		for (IPersistable object: objects) {
+			try {
+				store.saveObject(object);
+			} catch (Throwable e) {
+				log.error("Error while saving " + object + " in " + store, e);
+			}
+		}
+	}
+	
+    /**
+     * Write modified objects to the filesystem periodically.
+     */
+	public void run() {
+		while (isAlive()) {
+			long start = System.currentTimeMillis();
+			if (!modifiedObjects.isEmpty()) {
+				Map<IPersistable, FilePersistence> objects;
+				// Get snapshot of currently modified objects.
+				synchronized (modifiedObjects) {
+					objects = new HashMap<IPersistable, FilePersistence>(modifiedObjects);
+					modifiedObjects.clear();
+					objectStores.clear();
+				}
+				
+				for (Map.Entry<IPersistable, FilePersistence> entry: objects.entrySet()) {
+					try {
+						entry.getValue().saveObject(entry.getKey());
+					} catch (Throwable e) {
+						log.error("Error while saving " + entry.getKey() + " in " + entry.getValue(), e);
+					}
+				}
+			}
+			long end = System.currentTimeMillis();
+			try {
+				long delay = storeInterval - (end - start);
+				if (delay > 0) {
+					Thread.sleep(delay);
+				}
+			} catch (InterruptedException e) {
+				// Ignore
+			}
+		}
+	}
+
+}
Index: /java/server/branches/clustering/src/org/red5/server/ClientRegistry.java
===================================================================
--- /java/server/branches/clustering/src/org/red5/server/ClientRegistry.java (revision 2000)
+++ /java/server/branches/clustering/src/org/red5/server/ClientRegistry.java (revision 2176)
@@ -91,4 +91,5 @@
 	 * @return             Collection of clients
 	 */
+	@SuppressWarnings("unchecked")
 	protected Collection<IClient> getClients() {
 		if (!hasClients()) {
Index: /java/server/branches/clustering/src/org/red5/server/ClientList.java
===================================================================
--- /java/server/branches/clustering/src/org/red5/server/ClientList.java (revision 1947)
+++ /java/server/branches/clustering/src/org/red5/server/ClientList.java (revision 2176)
@@ -26,3 +26,8 @@
 public class ClientList<E> extends ArrayList<E> implements ListMBean {
 
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -3127064371410565214L;
+
 }
Index: /java/server/branches/clustering/src/org/red5/server/Shutdown.java
===================================================================
--- /java/server/branches/clustering/src/org/red5/server/Shutdown.java (revision 2022)
+++ /java/server/branches/clustering/src/org/red5/server/Shutdown.java (revision 2176)
@@ -49,5 +49,5 @@
 			JMXServiceURL url = null;
 			JMXConnector jmxc = null;
-            HashMap env = null;
+            HashMap<String, Object> env = null;
 			
 			if (null == args || args.length < 1) {
@@ -62,5 +62,5 @@
 				
 				if (args.length > 1) {
-		            env = new HashMap(1);
+		            env = new HashMap<String, Object>(1);
 		            String[] credentials = new String[] {args[1], args[2]};
 		            env.put("jmx.remote.credentials", credentials);
Index: /java/server/branches/clustering/src/org/red5/server/BasicScope.java
===================================================================
--- /java/server/branches/clustering/src/org/red5/server/BasicScope.java (revision 2133)
+++ /java/server/branches/clustering/src/org/red5/server/BasicScope.java (revision 2176)
@@ -116,5 +116,5 @@
 	public void removeEventListener(IEventListener listener) {
 		listeners.remove(listener);
-		if (!keepOnDisconnect && ScopeUtils.isRoom(this) && isPersistent() && listeners.isEmpty()) {
+		if (!keepOnDisconnect && ScopeUtils.isRoom(this) && listeners.isEmpty()) {
 			// Delete empty rooms
 			parent.removeChildScope(this);
Index: /java/server/branches/clustering/src/org/red5/server/stream/DefaultStreamFilenameGenerator.java
===================================================================
--- /java/server/branches/clustering/src/org/red5/server/stream/DefaultStreamFilenameGenerator.java (revision 1900)
+++ /java/server/branches/clustering/src/org/red5/server/stream/DefaultStreamFilenameGenerator.java (revision 2176)
@@ -67,3 +67,12 @@
 	}
 
+    /**
+     * The default filenames are relative to the scope path, so always return <code>false</code>.
+     * 
+     * @return	always <code>false</code>
+     */
+	public boolean resolvesToAbsolutePath() {
+		return false;
+	}
+
 }
Index: /java/server/branches/clustering/src/org/red5/server/stream/ServerStream.java
===================================================================
--- /java/server/branches/clustering/src/org/red5/server/stream/ServerStream.java (revision 1984)
+++ /java/server/branches/clustering/src/org/red5/server/stream/ServerStream.java (revision 2176)
@@ -321,15 +321,21 @@
 		
 		String filename = generator.generateFilename(scope, name, ".flv", GenerationType.RECORD);
-		Resource res = scope.getContext().getResource(filename);
+		// Get file for that filename
+		File file;
+		if (generator.resolvesToAbsolutePath()) {
+			file = new File(filename);
+		} else {
+			file = scope.getContext().getResource(filename).getFile();
+		}
 		if (!isAppend) {
-			if (res.exists()) {
+			if (file.exists()) {
 				// Per livedoc of FCS/FMS:
 				// When "live" or "record" is used,
 				// any previously recorded stream with the same stream URI is deleted.
-				if (!res.getFile().delete())
+				if (!file.delete())
 					throw new IOException("file could not be deleted");
 			}
 		} else {
-			if (!res.exists()) {
+			if (!file.exists()) {
 				// Per livedoc of FCS/FMS:
 				// If a recorded stream at the same URI does not already exist,
@@ -339,8 +345,7 @@
 		}
 
-		if (!res.exists()) {
+		if (!file.exists()) {
 			// Make sure the destination directory exists
-				try {
-			String path = res.getFile().getAbsolutePath();
+			String path = file.getAbsolutePath();
 			int slashPos = path.lastIndexOf(File.separator);
 			if (slashPos != -1) {
@@ -351,18 +356,14 @@
 				tmp.mkdirs();
 			}
-				} catch (IOException err) {
-					log.error("Could not create destination directory.", err);
-				}
-				res = scope.getResource(filename);
-		}
-
-		if (!res.exists()) {
-				if (!res.getFile().canWrite()) {
-					log.warn("File cannot be written to "
-							+ res.getFile().getCanonicalPath());
-				}
-			res.getFile().createNewFile();
-		}
-		FileConsumer fc = new FileConsumer(scope, res.getFile());
+		}
+
+		if (!file.exists()) {
+			if (!file.canWrite()) {
+				log.warn("File cannot be written to "
+						+ file.getCanonicalPath());
+			}
+			file.createNewFile();
+		}
+		FileConsumer fc = new FileConsumer(scope, file);
 		Map<Object, Object> paramMap = new HashMap<Object, Object>();
 		if (isAppend) {
@@ -371,7 +372,7 @@
 			paramMap.put("mode", "record");
 		}
-			if (null == recordPipe) {
-				recordPipe = new InMemoryPushPushPipe();
-			}
+		if (null == recordPipe) {
+			recordPipe = new InMemoryPushPushPipe();
+		}
 		recordPipe.subscribe(fc, paramMap);
 		recordingFilename = filename;
Index: /java/server/branches/clustering/src/org/red5/server/stream/BroadcastScope.java
===================================================================
--- /java/server/branches/clustering/src/org/red5/server/stream/BroadcastScope.java (revision 1928)
+++ /java/server/branches/clustering/src/org/red5/server/stream/BroadcastScope.java (revision 2176)
@@ -168,5 +168,5 @@
      * @return                 <code>true</code> on success, <code>false</code> otherwise
      */
-    public synchronized boolean subscribe(IProvider provider, Map paramMap) {
+    public boolean subscribe(IProvider provider, Map paramMap) {
 		synchronized (pipe) {
             return !hasRemoved && pipe.subscribe(provider, paramMap);
Index: /java/server/branches/clustering/src/org/red5/server/stream/ProviderService.java
===================================================================
--- /java/server/branches/clustering/src/org/red5/server/stream/ProviderService.java (revision 2010)
+++ /java/server/branches/clustering/src/org/red5/server/stream/ProviderService.java (revision 2176)
@@ -101,6 +101,5 @@
 		File file = null;
 		try {
-			file = scope.getContext().getResources(getStreamFilename(scope, name))[0]
-					.getFile();
+			file = getStreamFile(scope, name);
 		} catch (IOException e) {
 			log.error("Problem getting file: " + name);
@@ -158,5 +157,5 @@
 	}
 
-	private String getStreamFilename(IScope scope, String name) {
+	private File getStreamFile(IScope scope, String name) throws IOException {
 		IStreamableFileFactory factory = (IStreamableFileFactory) ScopeUtils
 				.getScopeService(scope, IStreamableFileFactory.class);
@@ -177,5 +176,13 @@
 			ScopeUtils.getScopeService(scope, IStreamFilenameGenerator.class, DefaultStreamFilenameGenerator.class);
 		
-		return filenameGenerator.generateFilename(scope, name, GenerationType.PLAYBACK);
+		String filename = filenameGenerator.generateFilename(scope, name, GenerationType.PLAYBACK);
+		File file;
+		if (filenameGenerator.resolvesToAbsolutePath()) {
+			file = new File(filename);
+		} else {
+			file = scope.getContext().getResource(filename).getFile();
+		}
+		return file;
+
 	}
 
Index: /java/server/branches/clustering/src/org/red5/server/stream/ClientBroadcastStream.java
===================================================================
--- /java/server/branches/clustering/src/org/red5/server/stream/ClientBroadcastStream.java (revision 2034)
+++ /java/server/branches/clustering/src/org/red5/server/stream/ClientBroadcastStream.java (revision 2176)
@@ -229,6 +229,8 @@
 				&& (event.getType() != IEvent.Type.STREAM_CONTROL)
 				&& (event.getType() != IEvent.Type.STREAM_DATA) || closed) {
-			//ignored event
-			log.debug("dispatchEvent: " + event.getType());
+			// ignored event
+			if (log.isDebugEnabled()) {
+				log.debug("dispatchEvent: " + event.getType());
+			}
 			return;
 		}
@@ -505,5 +507,7 @@
 	public void saveAs(String name, boolean isAppend) throws IOException,
 			ResourceNotFoundException, ResourceExistException {
-		log.debug("SaveAs - name: " + name + " append: " + isAppend);
+		if (log.isDebugEnabled()) {
+			log.debug("SaveAs - name: " + name + " append: " + isAppend);
+		}
 		// Get stream scope
 		IStreamCapableConnection conn = getConnection();
@@ -521,18 +525,23 @@
 		String filename = generator.generateFilename(scope, name, ".flv",
 				GenerationType.RECORD);
-		// Get resource for that filename
-		Resource res = scope.getContext().getResource(filename);
+		// Get file for that filename
+		File file;
+		if (generator.resolvesToAbsolutePath()) {
+			file = new File(filename);
+		} else {
+			file = scope.getContext().getResource(filename).getFile();
+		}
 		// If append mode is on...
 		if (!isAppend) {
-			if (res.exists()) {
+			if (file.exists()) {
 				// Per livedoc of FCS/FMS:
 				// When "live" or "record" is used,
 				// any previously recorded stream with the same stream URI is deleted.
-				if (!res.getFile().delete()) {
+				if (!file.delete()) {
 					throw new IOException("file could not be deleted");
 				}
 			}
 		} else {
-			if (!res.exists()) {
+			if (!file.exists()) {
 				// Per livedoc of FCS/FMS:
 				// If a recorded stream at the same URI does not already exist,
@@ -542,7 +551,7 @@
 		}
 
-		if (!res.exists()) {
+		if (!file.exists()) {
 			// Make sure the destination directory exists
-			String path = res.getFile().getAbsolutePath();
+			String path = file.getAbsolutePath();
 			int slashPos = path.lastIndexOf(File.separator);
 			if (slashPos != -1) {
@@ -555,9 +564,11 @@
 		}
 
-		if (!res.exists()) {
-			res.getFile().createNewFile();
-		}
-		log.debug("Recording file: " + res.getFile().getCanonicalPath());
-		recordingFile = new FileConsumer(scope, res.getFile());
+		if (!file.exists()) {
+			file.createNewFile();
+		}
+		if (log.isDebugEnabled()) {
+			log.debug("Recording file: " + file.getCanonicalPath());
+		}
+		recordingFile = new FileConsumer(scope, file);
 		Map<Object, Object> paramMap = new HashMap<Object, Object>();
 		if (isAppend) {
@@ -691,5 +702,7 @@
 	 */
 	public void setPublishedName(String name) {
-		log.debug("setPublishedName: " + name);
+		if (log.isDebugEnabled()) {
+			log.debug("setPublishedName: " + name);
+		}
 		//check to see if we are setting the name to the same string
 		if (!name.equals(publishedName)) {
Index: /java/server/branches/clustering/src/org/red5/server/net/rtmp/RTMPMinaIoHandler.java
===================================================================
--- /java/server/branches/clustering/src/org/red5/server/net/rtmp/RTMPMinaIoHandler.java (revision 2133)
+++ /java/server/branches/clustering/src/org/red5/server/net/rtmp/RTMPMinaIoHandler.java (revision 2176)
@@ -148,5 +148,7 @@
 			out.put(in);
 			out.flip();
-			rtmp.setHandshake(out, 1, Constants.HANDSHAKE_SIZE);
+			// Skip first 8 bytes when comparing the handshake, they seem to
+			// be changed when connecting from a Mac client.
+			rtmp.setHandshake(out, 9, Constants.HANDSHAKE_SIZE-8);
 			//in.release();
 			session.write(out); 
Index: /java/server/branches/clustering/src/org/red5/server/net/rtmp/codec/RTMPProtocolDecoder.java
==========================

Note:
Diffs are chopped if more than 25k.
This is to get past the limit on the mailing list.



More information about the Red5commits mailing list