You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openmeetings.apache.org by so...@apache.org on 2018/10/13 16:23:15 UTC

[openmeetings] branch master updated: [OPENMEETINGS-1955] major recording related code is implemened

This is an automated email from the ASF dual-hosted git repository.

solomax pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/openmeetings.git


The following commit(s) were added to refs/heads/master by this push:
     new daab856  [OPENMEETINGS-1955] major recording related code is implemened
daab856 is described below

commit daab856baf073733f7304a11f35344a4fefb4f1d
Author: Maxim Solodovnik <so...@gmail.com>
AuthorDate: Sat Oct 13 23:23:00 2018 +0700

    [OPENMEETINGS-1955] major recording related code is implemened
---
 .../openmeetings/core/converter/BaseConverter.java |  13 +-
 .../core/converter/InterviewConverter.java         |  14 +-
 .../core/converter/RecordingConverter.java         |  28 +-
 .../openmeetings/core/data/file/FileProcessor.java |   4 +-
 .../core/data/record/listener/StreamListener.java  |  62 ----
 .../record/listener/async/BaseStreamWriter.java    | 198 ------------
 .../data/record/listener/async/CachedEvent.java    |  76 -----
 .../record/listener/async/StreamAudioWriter.java   | 211 -------------
 .../record/listener/async/StreamVideoWriter.java   |  82 -----
 .../org/apache/openmeetings/core/remote/KRoom.java |  84 ++++-
 .../apache/openmeetings/core/remote/KStream.java   | 141 +++++++--
 .../openmeetings/core/remote/KTestStream.java      |  45 +--
 .../openmeetings/core/remote/KurentoHandler.java   |  47 ++-
 .../core/service/RecordingService.java             | 347 ---------------------
 .../db/dao/record/RecordingChunkDao.java           |  51 ++-
 .../openmeetings/db/dao/server/SOAPLoginDao.java   |   5 +-
 .../openmeetings/db/dao/server/SessiondataDao.java |   5 +-
 .../apache/openmeetings/db/dao/user/UserDao.java   |   6 +-
 .../openmeetings/db/dto/room/InvitationDTO.java    |   5 +-
 .../openmeetings/db/dto/room/Whiteboards.java      |   5 +-
 .../openmeetings/db/entity/basic/Client.java       |  62 +---
 .../openmeetings/db/entity/basic/IClient.java      |  54 ----
 .../openmeetings/db/entity/basic/WsClient.java     |   4 +-
 .../db/entity/record/RecordingChunk.java           | 103 ++----
 .../openmeetings/db/manager/IClientManager.java    |   3 +-
 .../openmeetings/db/util/ws/RoomMessage.java       |  13 +-
 .../openmeetings/db/util/ws/TextRoomMessage.java   |   4 +-
 .../apache/openmeetings/backup/BackupImport.java   |  17 +-
 .../openmeetings/backup/converter/WbConverter.java |   4 +-
 .../org/apache/openmeetings/screenshare/Core.java  |   4 +-
 .../service/calendar/caldav/IcalUtils.java         |   3 +-
 .../service/room/InvitationManager.java            |   4 +-
 .../org/apache/openmeetings/util/OmFileHelper.java |  13 +-
 .../apache/openmeetings/util/mail/IcalHandler.java |   5 +-
 .../web/admin/connection/ConnectionsPanel.java     |  48 +--
 .../openmeetings/web/admin/rooms/RoomForm.java     |   6 +-
 .../openmeetings/web/admin/users/UserForm.java     |   4 +-
 .../apache/openmeetings/web/app/ClientManager.java |  28 +-
 .../apache/openmeetings/web/app/UserManager.java   |   4 +-
 .../openmeetings/web/common/InvitationForm.java    |   4 +-
 .../web/common/tree/FileTreePanel.java             |   6 +-
 .../web/pages/auth/ForgetPasswordDialog.java       |   4 +-
 .../apache/openmeetings/web/room/RoomPanel.java    |  44 +--
 .../openmeetings/web/room/menu/RoomMenuPanel.java  |  22 +-
 .../openmeetings/web/room/raw-video-manager.js     |   2 +-
 .../apache/openmeetings/web/room/raw-video-util.js |   8 -
 .../openmeetings/web/room/wb/InterviewWbPanel.java |  17 +-
 .../apache/openmeetings/web/room/wb/WbPanel.java   |   6 +-
 .../web/util/CallbackFunctionHelper.java           |   5 +-
 .../apache/openmeetings/AbstractJUnitDefaults.java |  14 +-
 .../calendar/TestAppointmentAddAppointment.java    |   4 +-
 .../openmeetings/calendar/TestSendIcalMessage.java |   5 +-
 .../openmeetings/core/file/TestFileProcessor.java  |   4 +-
 .../apache/openmeetings/user/TestUserContact.java  |   6 +-
 .../apache/openmeetings/user/TestUserGroup.java    |   4 +-
 .../org/apache/openmeetings/web/TestCalendar.java  |   4 +-
 .../openmeetings/web/pages/auth/TestLoginUI.java   |   8 +-
 .../webservice/AbstractWebServiceTest.java         |   6 +-
 .../webservice/TestCalendarService.java            |  12 +-
 .../openmeetings/webservice/TestErrorService.java  |   4 +-
 .../webservice/TestRecordingService.java           |   4 +-
 .../openmeetings/webservice/TestRoomService.java   |   6 +-
 .../openmeetings/webservice/TestUserService.java   |   4 +-
 63 files changed, 508 insertions(+), 1507 deletions(-)

diff --git a/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/BaseConverter.java b/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/BaseConverter.java
index 9ff7d67..8281646 100644
--- a/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/BaseConverter.java
+++ b/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/BaseConverter.java
@@ -20,9 +20,7 @@ package org.apache.openmeetings.core.converter;
 
 import static org.apache.commons.io.FileUtils.copyFile;
 import static org.apache.commons.lang3.math.NumberUtils.toInt;
-import static org.apache.openmeetings.core.data.record.listener.async.BaseStreamWriter.TIME_TO_WAIT_FOR_FRAME;
 import static org.apache.openmeetings.util.CalendarHelper.formatMillis;
-import static org.apache.openmeetings.util.OmFileHelper.EXTENSION_FLV;
 import static org.apache.openmeetings.util.OmFileHelper.EXTENSION_PNG;
 import static org.apache.openmeetings.util.OmFileHelper.getRecordingChunk;
 import static org.apache.openmeetings.util.OmFileHelper.getStreamsHibernateDir;
@@ -50,7 +48,6 @@ import org.apache.openmeetings.db.entity.file.BaseFileItem;
 import org.apache.openmeetings.db.entity.record.Recording;
 import org.apache.openmeetings.db.entity.record.RecordingChunk;
 import org.apache.openmeetings.db.entity.record.RecordingChunk.Status;
-import org.apache.openmeetings.util.OmFileHelper;
 import org.apache.openmeetings.util.process.ProcessHelper;
 import org.apache.openmeetings.util.process.ProcessResult;
 import org.apache.openmeetings.util.process.ProcessResultList;
@@ -63,6 +60,8 @@ public abstract class BaseConverter {
 	private static final Logger log = LoggerFactory.getLogger(BaseConverter.class);
 	private static final Pattern p = Pattern.compile("\\d{2,5}(x)\\d{2,5}");
 	public static final String EXEC_EXT = System.getProperty("os.name").toUpperCase().indexOf("WINDOWS") < 0 ? "" : ".exe";
+	private static final int MINUTE_MULTIPLIER = 60 * 1000;
+	public static final int TIME_TO_WAIT_FOR_FRAME = 15 * MINUTE_MULTIPLIER;
 
 	@Autowired
 	protected ConfigurationDao cfgDao;
@@ -249,7 +248,7 @@ public abstract class BaseConverter {
 
 				chunk = waitForTheStream(chunkId);
 
-				File inputFlvFile = new File(streamFolder, OmFileHelper.getName(chunk.getStreamName(), EXTENSION_FLV));
+				File inputFlvFile = getRecordingChunk(chunk.getRecording().getRoomId(), chunk.getStreamName());
 
 				File outputWav = new File(streamFolder, chunk.getStreamName() + "_WAVE.wav");
 
@@ -266,7 +265,6 @@ public abstract class BaseConverter {
 				}
 
 				if (outputWav.exists() && outputWav.length() != 0) {
-					chunk.setAudioValid(true);
 					// Strip Wave to Full Length
 					File outputGapFullWav = outputWav;
 
@@ -312,10 +310,10 @@ public abstract class BaseConverter {
 					File outputFullWav = new File(streamFolder, hashFileFullName);
 
 					// Calculate delta at beginning
-					double startPad = diffSeconds(chunk.getRecordStart(), recording.getRecordStart());
+					double startPad = diffSeconds(chunk.getStart(), recording.getRecordStart());
 
 					// Calculate delta at ending
-					double endPad = diffSeconds(recording.getRecordEnd(), chunk.getRecordEnd());
+					double endPad = diffSeconds(recording.getRecordEnd(), chunk.getEnd());
 
 					addSoxPad(logs, "addStartEndToAudio", startPad, endPad, outputGapFullWav, outputFullWav);
 
@@ -327,7 +325,6 @@ public abstract class BaseConverter {
 					if (!outputFullWav.exists()) {
 						throw new ConversionException("Audio File does not exist , could not extract the Audio correctly");
 					}
-					chunk.setFullWavAudioData(hashFileFullName);
 
 					// Finally add it to the row!
 					waveFiles.add(outputFullWav);
diff --git a/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/InterviewConverter.java b/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/InterviewConverter.java
index 6e16afb..0816a9b 100644
--- a/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/InterviewConverter.java
+++ b/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/InterviewConverter.java
@@ -18,6 +18,7 @@
  */
 package org.apache.openmeetings.core.converter;
 
+import static java.util.UUID.randomUUID;
 import static org.apache.openmeetings.util.CalendarHelper.formatMillis;
 import static org.apache.openmeetings.util.OmFileHelper.getRecordingChunk;
 
@@ -29,11 +30,10 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
-import java.util.UUID;
 import java.util.stream.Collectors;
 
-import org.apache.openmeetings.db.dao.record.RecordingDao;
 import org.apache.openmeetings.db.dao.record.RecordingChunkDao;
+import org.apache.openmeetings.db.dao.record.RecordingDao;
 import org.apache.openmeetings.db.entity.record.Recording;
 import org.apache.openmeetings.db.entity.record.RecordingChunk;
 import org.apache.openmeetings.util.OmFileHelper;
@@ -74,7 +74,7 @@ public class InterviewConverter extends BaseConverter implements IRecordingConve
 
 			log.debug("recording {}", r.getId());
 			if (Strings.isEmpty(r.getHash())) {
-				r.setHash(UUID.randomUUID().toString());
+				r.setHash(randomUUID().toString());
 			}
 			r.setStatus(Recording.Status.CONVERTING);
 			r = recordingDao.update(r);
@@ -95,7 +95,7 @@ public class InterviewConverter extends BaseConverter implements IRecordingConve
 			Map<String, List<RecordingChunk>> cunksBySid = chunks.stream().collect(
 					Collectors.groupingBy(RecordingChunk::getSid
 					, () -> new LinkedHashMap<>()
-					, Collectors.collectingAndThen(Collectors.toList(), l -> l.stream().sorted(Comparator.comparing(RecordingChunk::getRecordStart)).collect(Collectors.toList()))));
+					, Collectors.collectingAndThen(Collectors.toList(), l -> l.stream().sorted(Comparator.comparing(RecordingChunk::getStart)).collect(Collectors.toList()))));
 			List<String> pods = new ArrayList<>();
 			int N = pods.size();
 			for (Entry<String, List<RecordingChunk>> e : cunksBySid.entrySet()) {
@@ -116,13 +116,13 @@ public class InterviewConverter extends BaseConverter implements IRecordingConve
 						ProcessResult res = ProcessHelper.executeScript(String.format("checkFlvPod_%s_%s", N, parts.size()), args, true);
 						logs.add(res);
 						if (!res.isWarn()) {
-							long diff = diff(chunk.isAudioOnly() ? chunk.getRecordEnd() : chunk.getRecordStart(), pStart);
+							long diff = diff(chunk.isAudioOnly() ? chunk.getEnd() : chunk.getStart(), pStart);
 							//createBlankPod(id, streamFolder, interviewCam, diff, logs, pods, parts);
 							PodPart.add(parts, diff);
 							if (!chunk.isAudioOnly()) {
-								parts.add(new PodPart(path, diff(chunk.getRecordEnd(), chunk.getRecordStart())));
+								parts.add(new PodPart(path, diff(chunk.getEnd(), chunk.getStart())));
 							}
-							pStart = chunk.getRecordEnd();
+							pStart = chunk.getEnd();
 						}
 					} else {
 						log.debug("Meta FLV doesn't exist: {}", flv);
diff --git a/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/RecordingConverter.java b/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/RecordingConverter.java
index 656cb62..c7d33d2 100644
--- a/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/RecordingConverter.java
+++ b/openmeetings-core/src/main/java/org/apache/openmeetings/core/converter/RecordingConverter.java
@@ -18,21 +18,20 @@
  */
 package org.apache.openmeetings.core.converter;
 
+import static java.util.UUID.randomUUID;
 import static org.apache.openmeetings.util.CalendarHelper.formatMillis;
-import static org.apache.openmeetings.util.OmFileHelper.EXTENSION_FLV;
+import static org.apache.openmeetings.util.OmFileHelper.getRecordingChunk;
 
 import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
-import java.util.UUID;
 
-import org.apache.openmeetings.db.dao.record.RecordingDao;
 import org.apache.openmeetings.db.dao.record.RecordingChunkDao;
+import org.apache.openmeetings.db.dao.record.RecordingDao;
 import org.apache.openmeetings.db.entity.record.Recording;
 import org.apache.openmeetings.db.entity.record.RecordingChunk;
 import org.apache.openmeetings.db.entity.record.RecordingChunk.Status;
-import org.apache.openmeetings.util.OmFileHelper;
 import org.apache.openmeetings.util.process.ProcessResultList;
 import org.apache.wicket.util.string.Strings;
 import org.slf4j.Logger;
@@ -64,34 +63,33 @@ public class RecordingConverter extends BaseConverter implements IRecordingConve
 
 			File streamFolder = getStreamFolder(r);
 
-			RecordingChunk screenMetaData = chunkDao.getScreenByRecording(r.getId());
+			RecordingChunk screenChunk = chunkDao.getScreenByRecording(r.getId());
 
-			if (screenMetaData == null) {
+			if (screenChunk == null) {
 				throw new ConversionException("screenMetaData is Null recordingId " + r.getId());
 			}
 
-			if (screenMetaData.getStreamStatus() == Status.NONE) {
-				printChunkInfo(screenMetaData, "StartConversion");
+			if (screenChunk.getStreamStatus() == Status.NONE) {
+				printChunkInfo(screenChunk, "StartConversion");
 				throw new ConversionException("Stream has not been started, error in recording");
 			}
 			if (Strings.isEmpty(r.getHash())) {
-				r.setHash(UUID.randomUUID().toString());
+				r.setHash(randomUUID().toString());
 			}
 			r.setStatus(Recording.Status.CONVERTING);
 			r = recordingDao.update(r);
 
-			screenMetaData = waitForTheStream(screenMetaData.getId());
+			screenChunk = waitForTheStream(screenChunk.getId());
 
 			// Merge Wave to Full Length
-			File wav = new File(streamFolder, screenMetaData.getStreamName() + "_FINAL_WAVE.wav");
+			File wav = new File(streamFolder, screenChunk.getStreamName() + "_FINAL_WAVE.wav");
 			createWav(r, logs, streamFolder, waveFiles, wav, null);
 
-			screenMetaData.setFullWavAudioData(wav.getName());
-			chunkDao.update(screenMetaData);
+			chunkDao.update(screenChunk);
 
 			// Merge Audio with Video / Calculate resulting FLV
 
-			String inputScreenFullFlv = new File(streamFolder, OmFileHelper.getName(screenMetaData.getStreamName(), EXTENSION_FLV)).getCanonicalPath();
+			String inputScreenFullFlv = getRecordingChunk(r.getRoomId(), screenChunk.getStreamName()).getCanonicalPath();
 
 			// ffmpeg -vcodec flv -qscale 9.5 -r 25 -ar 22050 -ab 32k -s 320x240
 			// -i 65318fb5c54b1bc1b1bca077b493a914_28_12_2009_23_38_17_FINAL_WAVE.wav
@@ -114,7 +112,7 @@ public class RecordingConverter extends BaseConverter implements IRecordingConve
 			r.setHeight(flvHeight);
 
 			String mp4path = convertToMp4(r, Arrays.asList(
-					"-itsoffset", formatMillis(diff(screenMetaData.getRecordStart(), r.getRecordStart())),
+					"-itsoffset", formatMillis(diff(screenChunk.getStart(), r.getRecordStart())),
 					"-i", inputScreenFullFlv, "-i", wav.getCanonicalPath()
 					), logs);
 
diff --git a/openmeetings-core/src/main/java/org/apache/openmeetings/core/data/file/FileProcessor.java b/openmeetings-core/src/main/java/org/apache/openmeetings/core/data/file/FileProcessor.java
index 05438a9..b8a50f1 100644
--- a/openmeetings-core/src/main/java/org/apache/openmeetings/core/data/file/FileProcessor.java
+++ b/openmeetings-core/src/main/java/org/apache/openmeetings/core/data/file/FileProcessor.java
@@ -18,13 +18,13 @@
  */
 package org.apache.openmeetings.core.data.file;
 
+import static java.util.UUID.randomUUID;
 import static org.apache.commons.io.FileUtils.copyFile;
 import static org.apache.commons.io.FileUtils.copyInputStreamToFile;
 import static org.apache.openmeetings.util.OmFileHelper.getFileExt;
 
 import java.io.File;
 import java.io.InputStream;
-import java.util.UUID;
 
 import org.apache.openmeetings.core.converter.DocumentConverter;
 import org.apache.openmeetings.core.converter.ImageConverter;
@@ -59,7 +59,7 @@ public class FileProcessor {
 		ProcessResultList logs = new ProcessResultList();
 		// Generate a random string to prevent any problems with
 		// foreign characters and duplicates
-		String hash = UUID.randomUUID().toString();
+		String hash = randomUUID().toString();
 
 		File temp = null;
 		try {
diff --git a/openmeetings-core/src/main/java/org/apache/openmeetings/core/data/record/listener/StreamListener.java b/openmeetings-core/src/main/java/org/apache/openmeetings/core/data/record/listener/StreamListener.java
deleted file mode 100644
index db65232..0000000
--- a/openmeetings-core/src/main/java/org/apache/openmeetings/core/data/record/listener/StreamListener.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * 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.apache.openmeetings.core.data.record.listener;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class StreamListener /*implements IStreamListener*/ {
-	private static final Logger log = LoggerFactory.getLogger(StreamListener.class);
-
-	/*
-	private final BaseStreamWriter streamWriter;
-
-	public StreamListener(boolean isAudio, String streamName, IScope scope, Long metaDataId,
-			boolean isScreenData, boolean isInterview) {
-		streamWriter = isAudio
-			? new StreamAudioWriter(streamName, scope, metaDataId, isScreenData, isInterview)
-			: new StreamVideoWriter(streamName, scope, metaDataId, isScreenData);
-	}
-
-	@Override
-	public void packetReceived(IBroadcastStream broadcastStream, IStreamPacket streampacket) {
-		try {
-			CachedEvent cachedEvent = new CachedEvent();
-			cachedEvent.setData(streampacket.getData().duplicate());
-			cachedEvent.setDataType(streampacket.getDataType());
-			cachedEvent.setTimestamp(streampacket.getTimestamp());
-			cachedEvent.setCurrentTime(new Date());
-			if (streampacket instanceof VideoData) {
-				cachedEvent.setFrameType(((VideoData) streampacket).getFrameType());
-			}
-
-			if (log.isTraceEnabled()) {
-				log.trace("##REC:: Packet recieved. type: {} frame type: {}", cachedEvent.getDataType(), cachedEvent.getFrameType());
-			}
-			streamWriter.append(cachedEvent);
-		} catch (Exception e) {
-			log.error("##REC:: [packetReceived]", e);
-		}
-	}
-
-	public void closeStream() {
-		streamWriter.stop();
-	}
-	*/
-}
diff --git a/openmeetings-core/src/main/java/org/apache/openmeetings/core/data/record/listener/async/BaseStreamWriter.java b/openmeetings-core/src/main/java/org/apache/openmeetings/core/data/record/listener/async/BaseStreamWriter.java
deleted file mode 100644
index 724b452..0000000
--- a/openmeetings-core/src/main/java/org/apache/openmeetings/core/data/record/listener/async/BaseStreamWriter.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * 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.apache.openmeetings.core.data.record.listener.async;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public abstract class BaseStreamWriter implements Runnable {
-	private static final Logger log = LoggerFactory.getLogger(BaseStreamWriter.class);
-	private static final int MINUTE_MULTIPLIER = 60 * 1000;
-	public static final int TIME_TO_WAIT_FOR_FRAME = 15 * MINUTE_MULTIPLIER;
-	protected int startTimeStamp = -1;
-	protected long initialDelta = 0;
-
-	// thread is running
-	private boolean running = false;
-	// thread is stopped
-	private boolean stopping = false;
-	// thread will be stopped as soon as the queue is empty
-	private boolean dostopping = false;
-
-	/*
-	protected ITagWriter writer = null;
-
-	protected Long metaDataId = null;
-
-	protected Date startedSessionTimeDate = null;
-
-	protected File file;
-
-	protected IScope scope;
-
-	protected boolean isScreenData = false;
-
-	protected String streamName = "";
-	protected final RecordingChunkDao metaDataDao;
-	private final BlockingQueue<CachedEvent> queue = new LinkedBlockingQueue<>();
-
-	public BaseStreamWriter(String streamName, IScope scope, Long metaDataId, boolean isScreenData) {
-		startedSessionTimeDate = new Date();
-		this.isScreenData = isScreenData;
-		this.streamName = streamName;
-		this.metaDataId = metaDataId;
-		this.metaDataDao = getApp().getOmBean(RecordingChunkDao.class);
-		this.scope = scope;
-		try {
-			init();
-		} catch (IOException ex) {
-			log.error("##REC:: [BaseStreamWriter] Could not init Thread", ex);
-		}
-		RecordingChunk metaData = metaDataDao.get(metaDataId);
-		metaData.setStreamStatus(Status.STARTED);
-		metaDataDao.update(metaData);
-
-		open();
-	}
-	*/
-
-	/*
-	 * Initialization
-	 *
-	 * @throws IOException
-	 *             I/O exception
-	 */
-	/*
-	private void init() throws IOException {
-		file = new File(OmFileHelper.getStreamsSubDir(scope.getName()), OmFileHelper.getName(streamName, EXTENSION_FLV));
-
-		IStreamableFileFactory factory = (IStreamableFileFactory) ScopeUtils.getScopeService(scope, IStreamableFileFactory.class,
-				StreamableFileFactory.class);
-
-		if (!file.isFile()) {
-			// Maybe the (previously existing) file has been deleted
-			file.createNewFile();
-
-		} else if (!file.canWrite()) {
-			throw new IOException("The file is read-only");
-		}
-
-		IStreamableFileService service = factory.getService(file);
-		IStreamableFile flv = service.getStreamableFile(file);
-		writer = flv.getWriter();
-	}
-
-	private void open() {
-		running = true;
-		new Thread(this, "Recording " + file.getName()).start();
-	}
-
-	public void stop() {
-		dostopping = true;
-	}
-	*/
-	@Override
-	public void run() {
-		/*
-		log.debug("##REC:: stream writer started");
-		long lastPackedRecieved = System.currentTimeMillis() + TIME_TO_WAIT_FOR_FRAME;
-		long counter = 0;
-		while (!stopping) {
-			try {
-				CachedEvent item = queue.poll(100, TimeUnit.MICROSECONDS);
-				if (item != null) {
-					log.trace("##REC:: got packet");
-					lastPackedRecieved = System.currentTimeMillis();
-					if (dostopping) {
-						log.trace("metadatId: {} :: Recording stopped but still packets to write to file!", metaDataId);
-					}
-
-					packetReceived(item);
-				} else if (dostopping || lastPackedRecieved + TIME_TO_WAIT_FOR_FRAME < System.currentTimeMillis()) {
-					log.debug("##REC:: none packets received for: {} minutes, exiting", (System.currentTimeMillis() - lastPackedRecieved) / MINUTE_MULTIPLIER);
-					stopping = true;
-					closeStream();
-				}
-				if (++counter % 5000 == 0) {
-					log.debug("##REC:: Stream writer is still listening:: {}", file.getName());
-				}
-			} catch (InterruptedException e) {
-				log.error("##REC:: [run]", e);
-			}
-		}
-		log.debug("##REC:: stream writer stopped");
-		 */
-	}
-
-	/**
-	 * Write the actual packet data to the disk and do calculate any needed additional information
-	 *
-	 * @param streampacket - received packet
-	 */
-	public abstract void packetReceived(CachedEvent streampacket);
-
-	protected abstract void internalCloseStream();
-
-	/**
-	 * called when the stream is finished written on the disk
-	 */
-	/*
-	public void closeStream() {
-		try {
-			writer.close();
-		} catch (Exception err) {
-			log.error("[closeStream, close writer]", err);
-		}
-		internalCloseStream();
-		// Write the complete Bit to the meta data, the converter task will wait for this bit!
-		try {
-			RecordingChunk metaData = metaDataDao.get(metaDataId);
-			log.debug("##REC:: Stream Status was: {} has been written for: {}", metaData.getStreamStatus(), metaDataId);
-			metaData.setStreamStatus(Status.STOPPED);
-			metaDataDao.update(metaData);
-		} catch (Exception err) {
-			log.error("##REC:: [closeStream, complete Bit]", err);
-		}
-	}
-
-	public void append(CachedEvent streampacket) {
-		if (!running) {
-			throw new IllegalStateException("Append called before the Thread was started!");
-		}
-		try {
-			queue.put(streampacket);
-			log.trace("##REC:: Q put, successful: {}", queue.size());
-		} catch (InterruptedException ignored) {
-			log.error("##REC:: [append]", ignored);
-		}
-	}
-
-	protected void write(int timeStamp, byte type, IoBuffer data) throws IOException {
-		log.trace("timeStamp :: {}", timeStamp);
-		ITag tag = new Tag();
-		tag.setDataType(type);
-
-		tag.setBodySize(data.limit());
-		tag.setTimestamp(timeStamp);
-		tag.setBody(data);
-
-		writer.writeTag(tag);
-	}
-	*/
-}
diff --git a/openmeetings-core/src/main/java/org/apache/openmeetings/core/data/record/listener/async/CachedEvent.java b/openmeetings-core/src/main/java/org/apache/openmeetings/core/data/record/listener/async/CachedEvent.java
deleted file mode 100644
index 9cfa329..0000000
--- a/openmeetings-core/src/main/java/org/apache/openmeetings/core/data/record/listener/async/CachedEvent.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * 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.apache.openmeetings.core.data.record.listener.async;
-
-import java.util.Date;
-
-import org.apache.mina.core.buffer.IoBuffer;
-
-public class CachedEvent/* implements IStreamPacket*/ {
-	private byte dataType;
-	private int timestamp; //this is the timeStamp, showing the time elapsed since the microphone was turned on
-	private IoBuffer data;
-	private Date currentTime; //this is the actually current timeStamp when the packet with audio data did enter the server
-	//private FrameType frameType = FrameType.UNKNOWN;
-
-	public Date getCurrentTime() {
-		return currentTime;
-	}
-
-	public void setCurrentTime(Date currentTime) {
-		this.currentTime = currentTime;
-	}
-
-	public void setDataType(byte dataType) {
-		this.dataType = dataType;
-	}
-
-	public void setTimestamp(int timestamp) {
-		this.timestamp = timestamp;
-	}
-
-	public void setData(IoBuffer data) {
-		this.data = data;
-	}
-
-	/*
-	@Override
-	public byte getDataType() {
-		return dataType;
-	}
-
-	@Override
-	public int getTimestamp() {
-		return timestamp;
-	}
-
-	@Override
-	public IoBuffer getData() {
-		return data;
-	}
-
-	public FrameType getFrameType() {
-		return frameType;
-	}
-
-	public void setFrameType(FrameType frameType) {
-		this.frameType = frameType;
-	}
-	*/
-}
diff --git a/openmeetings-core/src/main/java/org/apache/openmeetings/core/data/record/listener/async/StreamAudioWriter.java b/openmeetings-core/src/main/java/org/apache/openmeetings/core/data/record/listener/async/StreamAudioWriter.java
deleted file mode 100644
index 47b90961..0000000
--- a/openmeetings-core/src/main/java/org/apache/openmeetings/core/data/record/listener/async/StreamAudioWriter.java
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * 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.apache.openmeetings.core.data.record.listener.async;
-
-import java.util.Date;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class StreamAudioWriter extends BaseStreamWriter {
-	private static final Logger log = LoggerFactory.getLogger(StreamAudioWriter.class);
-
-	private int duration = 0;
-
-	private Integer lastTimeStamp = -1;
-	private Date lastcurrentTime = null;
-
-	private int lastStreamPacketTimeStamp = -1;
-
-	private long byteCount = 0;
-
-	// Autowire is not possible
-	//protected final RecordingMetaDeltaDao metaDeltaDao;
-
-	private boolean isInterview = false;
-
-	/*
-	public StreamAudioWriter(String streamName, IScope scope, Long metaDataId, boolean isScreenData, boolean isInterview) {
-		super(streamName, scope, metaDataId, isScreenData);
-
-		this.metaDeltaDao = getApp().getOmBean(RecordingMetaDeltaDao.class);
-		this.isInterview = isInterview;
-	}
-	*/
-	@Override
-	public void packetReceived(CachedEvent streampacket) {
-		/* FIXME TODO
-		try {
-			// We only care about audio at this moment
-			if (isInterview || TYPE_AUDIO == streampacket.getDataType()) {
-				if (streampacket.getTimestamp() <= 0) {
-					log.warn("##REC:: Negative TimeStamp");
-					return;
-				}
-				// we should not skip audio data in case it is Audio only interview
-				if ((isInterview || isScreenData) && startTimeStamp == -1 && KEYFRAME != streampacket.getFrameType()) {
-					//skip until keyframe
-					log.trace("##REC:: no KEYFRAME, skipping");
-					return;
-				}
-				IoBuffer data = streampacket.getData().asReadOnlyBuffer();
-				if (data.limit() == 0) {
-					log.trace("##REC:: data.limit() == 0 ");
-					return;
-				}
-
-				byteCount += data.limit();
-
-				lastcurrentTime = streampacket.getCurrentTime();
-				int timeStamp = streampacket.getTimestamp();
-				Date virtualTime = streampacket.getCurrentTime();
-
-				if (startTimeStamp == -1) {
-					// Calculate the delta between the initial start and the first audio-packet data
-
-					initialDelta = virtualTime.getTime() - startedSessionTimeDate.getTime();
-
-					RecordingMetaDelta metaDelta = new RecordingMetaDelta();
-
-					metaDelta.setDeltaTime(initialDelta);
-					metaDelta.setMetaDataId(metaDataId);
-					metaDelta.setTimeStamp(0);
-					metaDelta.setDebugStatus("INIT AUDIO");
-					metaDelta.setStartPadding(true);
-					metaDelta.setEndPadding(false);
-					metaDelta.setDataLengthPacket(data.limit());
-					metaDelta.setReceivedAudioDataLength(byteCount);
-					metaDelta.setStartTime(startedSessionTimeDate);
-					metaDelta.setPacketTimeStamp(streampacket.getTimestamp());
-
-					Long deltaTimeStamp = virtualTime.getTime() - startedSessionTimeDate.getTime();
-
-					metaDelta.setDuration(0);
-
-					Long missingTime = deltaTimeStamp - 0;
-
-					metaDelta.setMissingTime(missingTime);
-
-					metaDelta.setCurrentTime(virtualTime);
-					metaDelta.setDeltaTimeStamp(deltaTimeStamp);
-					metaDelta.setStartTimeStamp(startTimeStamp);
-
-					metaDeltaDao.add(metaDelta);
-
-					// That will be not bigger then long value
-					startTimeStamp = streampacket.getTimestamp();
-
-					RecordingChunk metaData = metaDataDao.get(metaDataId);
-					metaData.setRecordStart(virtualTime);
-					metaDataDao.update(metaData);
-				}
-
-				lastStreamPacketTimeStamp = streampacket.getTimestamp();
-
-				timeStamp -= startTimeStamp;
-
-				// Offset at the beginning is calculated above
-				long deltaTime = lastTimeStamp == -1 ? 0 : timeStamp - lastTimeStamp;
-
-				Long preLastTimeStamp = Long.parseLong(lastTimeStamp.toString());
-
-				lastTimeStamp = timeStamp;
-
-				if (deltaTime > 75) {
-					RecordingMetaDelta metaDelta = new RecordingMetaDelta();
-
-					metaDelta.setDeltaTime(deltaTime);
-					metaDelta.setMetaDataId(metaDataId);
-					metaDelta.setTimeStamp(timeStamp);
-					metaDelta.setDebugStatus("RUN AUDIO");
-					metaDelta.setStartPadding(false);
-					metaDelta.setLastTimeStamp(preLastTimeStamp);
-					metaDelta.setEndPadding(false);
-					metaDelta.setDataLengthPacket(data.limit());
-					metaDelta.setReceivedAudioDataLength(byteCount);
-					metaDelta.setStartTime(startedSessionTimeDate);
-					metaDelta.setPacketTimeStamp(streampacket.getTimestamp());
-
-					Date curDate = new Date();
-					Long deltaTimeStamp = curDate.getTime() - startedSessionTimeDate.getTime();
-
-					duration = Math.max(duration, timeStamp + writer.getOffset());
-					metaDelta.setDuration(duration);
-
-					Long missingTime = deltaTimeStamp - timeStamp;
-
-					metaDelta.setMissingTime(missingTime);
-
-					metaDelta.setCurrentTime(curDate);
-					metaDelta.setDeltaTimeStamp(deltaTimeStamp);
-					metaDelta.setStartTimeStamp(startTimeStamp);
-
-					metaDeltaDao.add(metaDelta);
-				}
-
-				log.trace("##REC:: timeStamp :: {}", timeStamp);
-				write(timeStamp, streampacket.getDataType(), data);
-			}
-		} catch (Exception e) {
-			log.error("##REC:: [packetReceived]", e);
-		}
-		*/
-	}
-
-	@Override
-	protected void internalCloseStream() {
-		/*
-		try {
-			// We do not add any End Padding or count the gaps for the
-			// Screen Data, cause there is no!
-
-			Date virtualTime = lastcurrentTime;
-			log.debug("##REC:: virtualTime: {}", virtualTime);
-			log.debug("##REC:: startedSessionTimeDate: {}", startedSessionTimeDate);
-
-			long deltaRecordingTime = virtualTime == null ? 0 : virtualTime.getTime() - startedSessionTimeDate.getTime();
-
-			log.debug("##REC:: lastTimeStamp :closeStream: {}", lastTimeStamp);
-			log.debug("##REC:: lastStreamPacketTimeStamp :closeStream: {}", lastStreamPacketTimeStamp);
-			log.debug("##REC:: deltaRecordingTime :closeStream: {}", deltaRecordingTime);
-
-			long deltaTimePaddingEnd = deltaRecordingTime - lastTimeStamp - initialDelta;
-
-			log.debug("##REC:: deltaTimePaddingEnd :: {}", deltaTimePaddingEnd);
-
-			RecordingMetaDelta metaDelta = new RecordingMetaDelta();
-
-			metaDelta.setDeltaTime(deltaTimePaddingEnd);
-			metaDelta.setMetaDataId(metaDataId);
-			metaDelta.setTimeStamp(lastTimeStamp);
-			metaDelta.setDebugStatus("END AUDIO");
-			metaDelta.setStartPadding(false);
-			metaDelta.setEndPadding(true);
-			metaDelta.setDataLengthPacket(null);
-			metaDelta.setReceivedAudioDataLength(byteCount);
-			metaDelta.setStartTime(startedSessionTimeDate);
-			metaDelta.setCurrentTime(new Date());
-
-			metaDeltaDao.add(metaDelta);
-		} catch (Exception err) {
-			log.error("##REC:: [internalCloseStream]", err);
-		}
-		*/
-	}
-}
diff --git a/openmeetings-core/src/main/java/org/apache/openmeetings/core/data/record/listener/async/StreamVideoWriter.java b/openmeetings-core/src/main/java/org/apache/openmeetings/core/data/record/listener/async/StreamVideoWriter.java
deleted file mode 100644
index 456fe98..0000000
--- a/openmeetings-core/src/main/java/org/apache/openmeetings/core/data/record/listener/async/StreamVideoWriter.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * 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.apache.openmeetings.core.data.record.listener.async;
-
-import java.util.Date;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class StreamVideoWriter extends BaseStreamWriter {
-	private static final Logger log = LoggerFactory.getLogger(StreamVideoWriter.class);
-	private Date startedSessionScreenTimeDate = null;
-
-	/*
-	public StreamVideoWriter(String streamName, IScope scope, Long metaDataId, boolean isScreenData) {
-		super(streamName, scope, metaDataId, isScreenData);
-	}
-	*/
-	@Override
-	public void packetReceived(CachedEvent streampacket) {
-		/*
-		try {
-			int timeStamp = streampacket.getTimestamp();
-			log.trace("incoming timeStamp :: " + timeStamp);
-			if (startTimeStamp == -1 && KEYFRAME != streampacket.getFrameType()) {
-				//skip until keyframe
-				log.trace("no KEYFRAME, skipping :: {}", streampacket.getFrameType());
-				return;
-			}
-			if (timeStamp <= 0) {
-				log.warn("Negative TimeStamp");
-				return;
-			}
-			IoBuffer data = streampacket.getData().asReadOnlyBuffer();
-			if (data.limit() == 0) {
-				log.trace("Data.limit() == 0");
-				return;
-			}
-			Date virtualTime = streampacket.getCurrentTime();
-
-			if (startedSessionScreenTimeDate == null) {
-				startedSessionScreenTimeDate = virtualTime;
-
-				RecordingChunk metaData = metaDataDao.get(metaDataId);
-				metaData.setRecordStart(virtualTime);
-				metaDataDao.update(metaData);
-			}
-
-			if (startTimeStamp == -1) {
-				// That will be not bigger then long value
-				startTimeStamp = timeStamp;
-			}
-			timeStamp -= startTimeStamp;
-
-			write(timeStamp, streampacket.getDataType(), data);
-		} catch (Exception e) {
-			log.error("[packetReceived]", e);
-		}
-		*/
-	}
-
-	@Override
-	protected void internalCloseStream() {
-		//should be empty for video writer
-	}
-}
diff --git a/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/KRoom.java b/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/KRoom.java
index 4d56ad7..465cfc8 100644
--- a/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/KRoom.java
+++ b/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/KRoom.java
@@ -21,25 +21,42 @@
  */
 package org.apache.openmeetings.core.remote;
 
+import static java.util.UUID.randomUUID;
+
+import java.io.IOException;
 import java.util.Collection;
+import java.util.Date;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicBoolean;
 
+import org.apache.openmeetings.core.util.WebSocketHelper;
+import org.apache.openmeetings.db.dao.record.RecordingChunkDao;
+import org.apache.openmeetings.db.dao.record.RecordingDao;
 import org.apache.openmeetings.db.entity.basic.Client;
 import org.apache.openmeetings.db.entity.basic.Client.StreamDesc;
+import org.apache.openmeetings.db.entity.file.BaseFileItem;
+import org.apache.openmeetings.db.entity.record.Recording;
+import org.apache.openmeetings.db.entity.room.Room;
+import org.apache.openmeetings.db.entity.user.User;
+import org.apache.openmeetings.db.util.ws.RoomMessage;
+import org.apache.openmeetings.util.CalendarPatterns;
 import org.kurento.client.Continuation;
 import org.kurento.client.MediaPipeline;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.github.openjson.JSONObject;
+
 public class KRoom {
-	private final static Logger log = LoggerFactory.getLogger(KRoom.class);
+	private static final Logger log = LoggerFactory.getLogger(KRoom.class);
 
 	private final Map<String, KStream> streams = new ConcurrentHashMap<>();
 	private final MediaPipeline pipeline;
 	private final Long roomId;
-	private final AtomicBoolean sharingStarted = new AtomicBoolean(false);
+	private final AtomicBoolean recordingStarted = new AtomicBoolean(false);
+	private Long recordingId = null;
+	private JSONObject recordingUser = new JSONObject();
 
 	public KRoom(Long roomId, MediaPipeline pipeline) {
 		this.roomId = roomId;
@@ -76,6 +93,69 @@ public class KRoom {
 		}
 	}
 
+	public boolean isRecording() {
+		return recordingStarted.get();
+	}
+
+	public JSONObject getRecordingUser() {
+		return new JSONObject(recordingUser.toString());
+	}
+
+	public void startRecording(Client c, RecordingDao recDao, RecordingChunkDao chunkDao) throws IOException {
+		if (recordingStarted.compareAndSet(false, true)) {
+			log.debug("##REC:: recording in room is started ::");
+			Room r = c.getRoom();
+			boolean interview = Room.Type.interview == r.getType();
+
+			Date now = new Date();
+
+			Recording rec = new Recording();
+
+			rec.setHash(randomUUID().toString());
+			rec.setName(String.format("%s %s", interview ? "Interview" : "Recording", CalendarPatterns.getDateWithTimeByMiliSeconds(new Date())));
+			User u = c.getUser();
+			recordingUser.put("login", u.getLogin());
+			recordingUser.put("firstName", u.getFirstname());
+			recordingUser.put("lastName", u.getLastname());
+			recordingUser.put("started", now.getTime());
+			Long ownerId = User.Type.contact == u.getType() ? u.getOwnerId() : u.getId();
+			rec.setInsertedBy(ownerId);
+			rec.setType(BaseFileItem.Type.Recording);
+			rec.setInterview(interview);
+
+			rec.setRoomId(roomId);
+			rec.setRecordStart(now);
+
+			rec.setOwnerId(ownerId);
+			rec.setStatus(Recording.Status.RECORDING);
+			rec = recDao.update(rec);
+			// Receive recordingId
+			recordingId = rec.getId();
+			log.debug("##REC:: recording created by USER: {}", ownerId);
+
+			for (final KStream stream : streams.values()) {
+				stream.startRecord(recordingId, chunkDao);
+			}
+
+			// Send notification to all users that the recording has been started
+			WebSocketHelper.sendRoom(new RoomMessage(roomId, u, RoomMessage.Type.recordingToggled));
+		}
+	}
+
+	public void stopRecording(Client c, RecordingDao recDao) {
+		if (recordingStarted.compareAndSet(true, false)) {
+			for (final KStream stream : streams.values()) {
+				stream.stopRecord();
+			}
+			Recording rec = recDao.get(recordingId);
+			rec.setRecordEnd(new Date());
+			rec = recDao.update(rec);
+
+			// Send notification to all users that the recording has been started
+			WebSocketHelper.sendRoom(new RoomMessage(roomId, c.getUser(), RoomMessage.Type.recordingToggled));
+		}
+	}
+
 	public void close(final KurentoHandler h) {
 		for (final KStream stream : streams.values()) {
 			stream.release(h);
diff --git a/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/KStream.java b/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/KStream.java
index 9186c88..5ac789b 100644
--- a/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/KStream.java
+++ b/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/KStream.java
@@ -21,16 +21,23 @@
  */
 package org.apache.openmeetings.core.remote;
 
+import static java.util.UUID.randomUUID;
 import static org.apache.openmeetings.core.remote.KurentoHandler.newKurentoMsg;
+import static org.apache.openmeetings.util.OmFileHelper.getRecUri;
+import static org.apache.openmeetings.util.OmFileHelper.getRecordingChunk;
 
+import java.io.IOException;
 import java.util.Map.Entry;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
 import org.apache.openmeetings.core.util.WebSocketHelper;
+import org.apache.openmeetings.db.dao.record.RecordingChunkDao;
 import org.apache.openmeetings.db.entity.basic.Client;
 import org.apache.openmeetings.db.entity.basic.Client.Activity;
 import org.apache.openmeetings.db.entity.basic.Client.StreamDesc;
+import org.apache.openmeetings.db.entity.basic.Client.StreamType;
+import org.apache.openmeetings.db.entity.record.RecordingChunk.Type;
 import org.apache.openmeetings.db.util.ws.RoomMessage;
 import org.apache.openmeetings.db.util.ws.TextRoomMessage;
 import org.kurento.client.Continuation;
@@ -38,7 +45,11 @@ import org.kurento.client.EventListener;
 import org.kurento.client.IceCandidate;
 import org.kurento.client.IceCandidateFoundEvent;
 import org.kurento.client.MediaPipeline;
+import org.kurento.client.MediaProfileSpecType;
 import org.kurento.client.MediaType;
+import org.kurento.client.RecorderEndpoint;
+import org.kurento.client.RecordingEvent;
+import org.kurento.client.StoppedEvent;
 import org.kurento.client.WebRtcEndpoint;
 import org.kurento.jsonrpc.JsonUtils;
 import org.slf4j.Logger;
@@ -52,9 +63,15 @@ public class KStream implements IKStream {
 	private final String sid;
 	private final String uid;
 	private final MediaPipeline pipeline;
+	private MediaProfileSpecType profile;
+	private RecorderEndpoint recorder;
 	private final Long roomId;
 	private WebRtcEndpoint outgoingMedia = null;
 	private final ConcurrentMap<String, WebRtcEndpoint> listeners = new ConcurrentHashMap<>();
+	private Long recordingId;
+	private Long chunkId;
+	private Type type;
+	private RecordingChunkDao chunkDao;
 
 	public KStream(final StreamDesc sd, MediaPipeline pipeline) {
 		this.pipeline = pipeline;
@@ -69,12 +86,38 @@ public class KStream implements IKStream {
 		if (outgoingMedia != null) {
 			release(h);
 		}
-		if ((sdpOffer.indexOf("m=audio") > -1 && !sd.hasActivity(Activity.broadcastA))
-				|| (sdpOffer.indexOf("m=video") > -1 && !sd.hasActivity(Activity.broadcastV)))
+		final boolean hasAudio = sd.hasActivity(Activity.broadcastA);
+		final boolean hasVideo = sd.hasActivity(Activity.broadcastV);
+		if ((sdpOffer.indexOf("m=audio") > -1 && !hasAudio)
+				|| (sdpOffer.indexOf("m=video") > -1 && !hasVideo))
 		{
 			log.warn("Broadcast started without enough rights");
 			return this;
 		}
+		if (StreamType.screen == sd.getType()) {
+			type = Type.SCREEN;
+		} else {
+			if (hasAudio && hasVideo) {
+				type = Type.AUDIO_VIDEO;
+			} else if (hasVideo) {
+				type = Type.VIDEO_ONLY;
+			} else {
+				type = Type.AUDIO_ONLY;
+			}
+		}
+		switch (type) {
+			case AUDIO_VIDEO:
+				profile = MediaProfileSpecType.WEBM;
+				break;
+			case AUDIO_ONLY:
+				profile = MediaProfileSpecType.WEBM_AUDIO_ONLY;
+				break;
+			case SCREEN:
+			case VIDEO_ONLY:
+			default:
+				profile = MediaProfileSpecType.WEBM_VIDEO_ONLY;
+				break;
+		}
 		outgoingMedia = createEndpoint(h, sd.getSid(), sd.getUid());
 		h.streamsByUid.put(uid, this);
 		addListener(h, sd.getSid(), sd.getUid(), sdpOffer);
@@ -87,22 +130,6 @@ public class KStream implements IKStream {
 		return this;
 	}
 
-	public String getSid() {
-		return sid;
-	}
-
-	public String getUid() {
-		return uid;
-	}
-
-	public Long getRoomId() {
-		return roomId;
-	}
-
-	public boolean contains(String uid) {
-		return this.uid.equals(uid) || listeners.containsKey(uid);
-	}
-
 	public void addListener(final KurentoHandler h, String sid, String uid, String sdpOffer) {
 		final boolean self = uid.equals(this.uid);
 		log.info("USER {}: have started {} in room {}", uid, self ? "broadcasting" : "receiving", roomId);
@@ -167,6 +194,60 @@ public class KStream implements IKStream {
 		return endpoint;
 	}
 
+	public void startRecord(Long recId, final RecordingChunkDao chunkDao) throws IOException {
+		this.chunkDao = chunkDao;
+		final String chunkUid = randomUUID().toString();
+		recordingId = recId;
+		recorder = new RecorderEndpoint.Builder(pipeline, getRecUri(getRecordingChunk(roomId, chunkUid)))
+				.stopOnEndOfStream()
+				.withMediaProfile(profile).build();
+
+		recorder.addRecordingListener(new EventListener<RecordingEvent>() {
+			@Override
+			public void onEvent(RecordingEvent event) {
+				chunkId = chunkDao.start(recordingId, type, chunkUid, sid);
+			}
+		});
+		recorder.addStoppedListener(new EventListener<StoppedEvent>() {
+			@Override
+			public void onEvent(StoppedEvent event) {
+				chunkDao.stop(chunkId);
+			}
+		});
+		switch (profile) {
+			case WEBM:
+				outgoingMedia.connect(recorder, MediaType.AUDIO);
+				outgoingMedia.connect(recorder, MediaType.VIDEO);
+				break;
+			case WEBM_VIDEO_ONLY:
+				outgoingMedia.connect(recorder, MediaType.VIDEO);
+				break;
+			case WEBM_AUDIO_ONLY:
+			default:
+				outgoingMedia.connect(recorder, MediaType.AUDIO);
+				break;
+		}
+		recorder.record(new Continuation<Void>() {
+			@Override
+			public void onSuccess(Void result) throws Exception {
+				log.info("Recording started successfully");
+			}
+
+			@Override
+			public void onError(Throwable cause) throws Exception {
+				log.error("Failed to start recording", cause);
+			}
+		});
+	}
+
+	public void stopRecord() {
+		recorder.stopAndWait();
+		releaseRecorder();
+		recordingId = null;
+		chunkId = null;
+		chunkDao = null;
+	}
+
 	public void remove(final Client c) {
 		WebRtcEndpoint point = listeners.remove(c.getUid());
 		if (point != null) {
@@ -208,9 +289,17 @@ public class KStream implements IKStream {
 			outgoingMedia.release();
 			outgoingMedia = null;
 		}
+		releaseRecorder();
 		h.streamsByUid.remove(uid);
 	}
 
+	private void releaseRecorder() {
+		if (recorder != null) {
+			recorder.release();
+			recorder = null;
+		}
+	}
+
 	public void addCandidate(IceCandidate candidate, String uid) {
 		if (this.uid.equals(uid)) {
 			outgoingMedia.addIceCandidate(candidate);
@@ -226,4 +315,20 @@ public class KStream implements IKStream {
 	private static JSONObject convert(com.google.gson.JsonObject o) {
 		return new JSONObject(o.toString());
 	}
+
+	public String getSid() {
+		return sid;
+	}
+
+	public String getUid() {
+		return uid;
+	}
+
+	public Long getRoomId() {
+		return roomId;
+	}
+
+	public boolean contains(String uid) {
+		return this.uid.equals(uid) || listeners.containsKey(uid);
+	}
 }
diff --git a/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/KTestStream.java b/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/KTestStream.java
index 912bbf3..19dbd4d 100644
--- a/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/KTestStream.java
+++ b/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/KTestStream.java
@@ -18,14 +18,15 @@
  */
 package org.apache.openmeetings.core.remote;
 
+import static java.util.UUID.randomUUID;
 import static org.apache.openmeetings.core.remote.KurentoHandler.newTestKurentoMsg;
 import static org.apache.openmeetings.core.remote.KurentoHandler.sendError;
+import static org.apache.openmeetings.util.OmFileHelper.EXTENSION_WEBM;
 import static org.apache.openmeetings.util.OmFileHelper.TEST_SETUP_PREFIX;
 import static org.apache.openmeetings.util.OmFileHelper.getStreamsDir;
 
 import java.io.File;
 import java.io.IOException;
-import java.util.UUID;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledFuture;
@@ -33,6 +34,7 @@ import java.util.concurrent.TimeUnit;
 
 import org.apache.openmeetings.core.util.WebSocketHelper;
 import org.apache.openmeetings.db.entity.basic.IWsClient;
+import org.apache.openmeetings.util.OmFileHelper;
 import org.kurento.client.Continuation;
 import org.kurento.client.EndOfStreamEvent;
 import org.kurento.client.ErrorEvent;
@@ -214,9 +216,8 @@ public class KTestStream implements IKStream {
 
 	private void initRecPath() {
 		try {
-			File f = new File(getStreamsDir(), String.format("%s%s.webm", TEST_SETUP_PREFIX, UUID.randomUUID()));
-			recPath = String.format("file://%s", f.getCanonicalPath());
-			log.info("Configured to record to {}", recPath);
+			File f = new File(getStreamsDir(), String.format("%s%s.%s", TEST_SETUP_PREFIX, randomUUID(), EXTENSION_WEBM));
+			recPath = OmFileHelper.getRecUri(f);
 		} catch (IOException e) {
 			log.error("Uexpected error while creating recording URI", e);
 		}
@@ -229,45 +230,15 @@ public class KTestStream implements IKStream {
 			webRtcEndpoint = null;
 		}
 		if (pipeline != null) {
-			pipeline.release(new Continuation<Void>() {
-				@Override
-				public void onSuccess(Void result) throws Exception {
-					log.info("Pipeline released successfully");
-				}
-
-				@Override
-				public void onError(Throwable cause) throws Exception {
-					log.info("Error releasing pipeline ", cause);
-				}
-			});
+			pipeline.release();
 			pipeline = null;
 		}
 		if (player != null) {
-			player.release(new Continuation<Void>() {
-				@Override
-				public void onSuccess(Void result) throws Exception {
-					log.info("Pipeline released successfully");
-				}
-
-				@Override
-				public void onError(Throwable cause) throws Exception {
-					log.info("Error releasing pipeline ", cause);
-				}
-			});
+			player.release();
 			player = null;
 		}
 		if (recorder != null) {
-			recorder.release(new Continuation<Void>() {
-				@Override
-				public void onSuccess(Void result) throws Exception {
-					log.info("Pipeline released successfully");
-				}
-
-				@Override
-				public void onError(Throwable cause) throws Exception {
-					log.info("Error releasing pipeline ", cause);
-				}
-			});
+			recorder.release();
 			recorder = null;
 		}
 		h.testsByUid.remove(uid);
diff --git a/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/KurentoHandler.java b/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/KurentoHandler.java
index 8cfad8c..8ee227f 100644
--- a/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/KurentoHandler.java
+++ b/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/KurentoHandler.java
@@ -19,15 +19,16 @@
  */
 package org.apache.openmeetings.core.remote;
 
+import static java.util.UUID.randomUUID;
 import static java.util.concurrent.TimeUnit.MILLISECONDS;
 
+import java.io.IOException;
 import java.security.InvalidKeyException;
 import java.security.NoSuchAlgorithmException;
 import java.util.Base64;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Map.Entry;
-import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
@@ -37,6 +38,8 @@ import javax.crypto.spec.SecretKeySpec;
 
 import org.apache.directory.api.util.Strings;
 import org.apache.openmeetings.core.util.WebSocketHelper;
+import org.apache.openmeetings.db.dao.record.RecordingChunkDao;
+import org.apache.openmeetings.db.dao.record.RecordingDao;
 import org.apache.openmeetings.db.entity.basic.Client;
 import org.apache.openmeetings.db.entity.basic.Client.Activity;
 import org.apache.openmeetings.db.entity.basic.Client.StreamDesc;
@@ -67,13 +70,13 @@ import com.github.openjson.JSONArray;
 import com.github.openjson.JSONObject;
 
 public class KurentoHandler {
-	private final static Logger log = LoggerFactory.getLogger(KurentoHandler.class);
-	private final static String MODE_TEST = "test";
-	private final static String TAG_KUID = "kuid";
-	private final static String TAG_MODE = "mode";
-	private final static String TAG_ROOM = "roomId";
-	private final static String HMAC_SHA1_ALGORITHM = "HmacSHA1";
-	public final static String KURENTO_TYPE = "kurento";
+	private static final Logger log = LoggerFactory.getLogger(KurentoHandler.class);
+	private static final String MODE_TEST = "test";
+	private static final String TAG_KUID = "kuid";
+	private static final String TAG_MODE = "mode";
+	private static final String TAG_ROOM = "roomId";
+	private static final String HMAC_SHA1_ALGORITHM = "HmacSHA1";
+	public static final String KURENTO_TYPE = "kurento";
 	private long checkTimeout = 200; //ms
 	private String kurentoWsUrl;
 	private String turnUrl;
@@ -89,12 +92,16 @@ public class KurentoHandler {
 
 	@Autowired
 	private IClientManager cm;
+	@Autowired
+	private RecordingDao recDao;
+	@Autowired
+	private RecordingChunkDao chunkDao;
 
 	public void init() {
 		try {
 			// TODO check connection, reconnect, listeners etc.
 			client = KurentoClient.create(kurentoWsUrl);
-			kuid = UUID.randomUUID().toString(); //FIXME TODO regenerate on re-connect
+			kuid = randomUUID().toString(); //FIXME TODO regenerate on re-connect
 			client.getServerManager().addObjectCreatedListener(new KWatchDog());
 		} catch (Exception e) {
 			log.error("Fail to create Kurento client", e);
@@ -298,6 +305,26 @@ public class KurentoHandler {
 		}
 	}
 
+	public void startRecording(Client c) {
+		try {
+			getRoom(c.getRoomId()).startRecording(c, recDao, chunkDao);
+		} catch (IOException e) {
+			log.error("Unexpected error while start record", e);
+		}
+	}
+
+	public void stopRecording(Client c) {
+		getRoom(c.getRoomId()).stopRecording(c, recDao);
+	}
+
+	public boolean isRecording(Long roomId) {
+		return getRoom(roomId).isRecording();
+	}
+
+	public JSONObject getRecordingUser(Long roomId) {
+		return getRoom(roomId).getRecordingUser();
+	}
+
 	public void leaveRoom(Client c) {
 		remove(c);
 		WebSocketHelper.sendAll(newKurentoMsg()
@@ -349,7 +376,7 @@ public class KurentoHandler {
 		}
 	}
 
-	public KRoom getRoom(Long roomId) {
+	private KRoom getRoom(Long roomId) {
 		log.debug("Searching for room {}", roomId);
 		KRoom room = rooms.get(roomId);
 
diff --git a/openmeetings-core/src/main/java/org/apache/openmeetings/core/service/RecordingService.java b/openmeetings-core/src/main/java/org/apache/openmeetings/core/service/RecordingService.java
deleted file mode 100644
index ad60706..0000000
--- a/openmeetings-core/src/main/java/org/apache/openmeetings/core/service/RecordingService.java
+++ /dev/null
@@ -1,347 +0,0 @@
-/*
- * 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.apache.openmeetings.core.service;
-
-import java.util.Date;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.apache.openmeetings.core.data.record.converter.InterviewConverterTask;
-import org.apache.openmeetings.core.data.record.converter.RecordingConverterTask;
-import org.apache.openmeetings.core.data.record.listener.StreamListener;
-import org.apache.openmeetings.db.dao.record.RecordingDao;
-import org.apache.openmeetings.db.dao.record.RecordingChunkDao;
-import org.apache.openmeetings.db.dao.user.UserDao;
-import org.apache.openmeetings.db.manager.IClientManager;
-import org.apache.openmeetings.util.CalendarPatterns;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
-@Service
-public class RecordingService {
-	private static final Logger log = LoggerFactory.getLogger(RecordingService.class);
-
-	/**
-	 * Stores a reference to all available listeners we need that reference, as the internal references stored with the
-	 * red5 stream object might be gone when the user closes the browser. But each listener has an asynchronous
-	 * component that needs to be closed no matter how the user leaves the application!
-	 */
-	private static final Map<Long, StreamListener> streamListeners = new ConcurrentHashMap<>();
-
-	@Autowired
-	private IClientManager clientManager;
-	@Autowired
-	private UserDao userDao;
-	@Autowired
-	private RecordingConverterTask recordingConverter;
-	@Autowired
-	private InterviewConverterTask interviewConverter;
-	@Autowired
-	private RecordingDao recordingDao;
-	@Autowired
-	private RecordingChunkDao metaDataDao;
-
-	private static String generateFileName(Long recordingId, String streamid) {
-		String dateString = CalendarPatterns.getTimeForStreamId(new Date());
-		return "rec_" + recordingId + "_stream_" + streamid + "_" + dateString;
-	}
-
-	/*
-	public void startRecording(final IScope scope, IClient client) {
-		try {
-			log.debug("##REC:: recordMeetingStream ::");
-			boolean isInterview = Room.Type.interview == client.getRoomType();
-			Long roomId = client.getRoomId();
-
-			Date now = new Date();
-
-			Recording recording = new Recording();
-
-			recording.setHash(UUID.randomUUID().toString());
-			recording.setName(String.format("%s %s", isInterview ? "Interview" : "Recording", CalendarPatterns.getDateWithTimeByMiliSeconds(new Date())));
-			Long ownerId = client.getUserId();
-			User u = userDao.get(ownerId);
-			if (u != null && User.Type.contact == u.getType()) {
-				ownerId = u.getOwnerId();
-				u = userDao.get(ownerId);
-			}
-			recording.setInsertedBy(ownerId);
-			recording.setType(Type.Recording);
-			recording.setInterview(isInterview);
-
-			recording.setRoomId(roomId);
-			recording.setRecordStart(now);
-
-			if (!isInterview) {
-				recording.setWidth(client.getWidth());
-				recording.setHeight(client.getHeight());
-			}
-
-			recording.setOwnerId(ownerId);
-			recording.setStatus(Recording.Status.RECORDING);
-			recording = recordingDao.update(recording);
-			// Receive recordingId
-			Long recordingId = recording.getId();
-			IClientUtil.setRecordingId(scope, recordingId);
-			log.debug("##REC:: recording created by USER: {}", ownerId);
-
-			// Update Client and set Flag
-			client.setRecordingStarted(true);
-			if (!(client instanceof Client)) {
-				Client c = clientManager.getBySid(client.getSid());
-				c.setRecordingId(recordingId);
-				c.setRecordingStarted(true);
-			}
-			streamClientManager.update(client);
-
-			// get all stream and start recording them
-			for (IConnection conn : scope.getClientConnections()) {
-				if (conn != null && conn instanceof IServiceCapableConnection) {
-					startStreamRecord(conn, recordingId, isInterview);
-				}
-			}
-			// Send notification to all users that the recording has been started
-			WebSocketHelper.sendRoom(new TextRoomMessage(roomId, u, RoomMessage.Type.recordingStarted
-					, new JSONObject().put("uid", client.getUid()).put("sid", client.getSid()).toString()));
-		} catch (Exception err) {
-			log.error("[startRecording]", err);
-		}
-	}
-
-	public void stopRecording(IScope scope, IClient client) {
-		try {
-			Long recordingId = IClientUtil.getRecordingId(scope);
-			IClientUtil.setRecordingId(scope, null);
-			log.debug("stopRecordAndSave {}, {}, ID: {}", client.getLogin(), client.getRemoteAddress(), recordingId);
-			if (recordingId == null) {
-				log.error("Unable to find recordingId on recording stop");
-				return;
-			}
-			Client recClient = null;
-			for (Client c : clientManager.listByRoom(client.getRoomId())) {
-				if (c.getRecordingId() != null) {
-					recClient = c;
-					break;
-				}
-			}
-			IClient stopClient;
-			if (recClient == null) {
-				log.warn("Unable to find Recording client");
-				stopClient = client;
-			} else {
-				stopClient = recClient;
-				// Store to database
-				recClient.setRecordingId(null);
-				recClient.setRecordingStarted(false);
-				streamClientManager.update(recClient);
-			}
-			WebSocketHelper.sendRoom(new TextRoomMessage(stopClient.getRoomId(), stopClient, RoomMessage.Type.recordingStoped, stopClient.getSid()));
-			// get all stream and stop recording them
-			for (IConnection conn : scope.getClientConnections()) {
-				if (conn != null && conn instanceof IServiceCapableConnection) {
-					StreamClient rcl = streamClientManager.get(IClientUtil.getId(conn.getClient()));
-					stopStreamRecord(scope, rcl);
-				}
-			}
-			recordingDao.updateEndTime(recordingId, new Date());
-
-			// Reset values
-			log.debug("recordingConverterTask {}", recordingConverter);
-
-			Recording recording = recordingDao.get(recordingId);
-			if (recording.isInterview()) {
-				interviewConverter.startConversionThread(recordingId);
-			} else {
-				recordingConverter.startConversionThread(recordingId);
-			}
-		} catch (Exception err) {
-			log.error("[-- stopRecording --]", err);
-		}
-	}
-	*/
-	/**
-	 * Start recording the published stream for the specified broadcast-Id
-	 *
-	 * @param conn
-	 * @param broadcastid
-	 * @param streamName
-	 * @param metaId
-	 * @param isScreenSharing
-	 * @param isInterview
-	 */
-	/*
-	private void addListener(IConnection conn, String broadcastid, String streamName, Long metaId, boolean isScreenSharing, boolean isInterview) {
-		log.debug("Recording show for: {}", conn.getScope().getContextPath());
-		log.debug("Name of CLient and Stream to be recorded: {}", broadcastid);
-		log.debug("Scope " + conn);
-		log.debug("Scope " + conn.getScope());
-		// Get a reference to the current broadcast stream.
-		ClientBroadcastStream stream = (ClientBroadcastStream) scopeAdapter.getBroadcastStream(conn.getScope(), broadcastid);
-
-		if (stream == null) {
-			log.debug("Unable to get stream: {}", streamName);
-			return;
-		}
-		// Save the stream to disk.
-		log.debug("### stream: [{}, name: {}, scope: {}, metaId: {}, sharing ? {}, interview ? {}]"
-				, stream, streamName, conn.getScope(), metaId, isScreenSharing, isInterview);
-		StreamListener streamListener = new StreamListener(!isScreenSharing, streamName, conn.getScope(), metaId, isScreenSharing, isInterview);
-
-		streamListeners.put(metaId, streamListener);
-		stream.addStreamListener(streamListener);
-	}*/
-
-	/**
-	 * Stops recording the publishing stream for the specified IConnection.
-	 *
-	 * @param scope
-	 * @param broadcastId
-	 * @param metaId
-	 */
-	/*
-	private void removeListener(IScope scope, String broadcastId, Long metaId) {
-		try {
-			log.debug("** removeListener: scope: {}, broadcastId: {} || {}", scope, broadcastId, scope.getContextPath());
-
-			IBroadcastStream stream = scopeAdapter.getBroadcastStream(scope, broadcastId);
-
-			// the stream can be null if the user just closes the browser
-			// without canceling the recording before leaving
-			// Iterate through all stream listeners and stop the appropriate
-			if (stream != null && stream.getStreamListeners() != null) {
-				for (IStreamListener iStreamListener : stream.getStreamListeners()) {
-					stream.removeStreamListener(iStreamListener);
-				}
-			}
-
-			if (metaId == null) {
-				// this should be fixed, can be useful for debugging, after all this is an error
-				// but we don't want the application to completely stop the process
-				log.error("recordingMetaDataId is null");
-				return;
-			}
-
-			StreamListener listenerAdapter = streamListeners.get(metaId);
-			log.debug("Stream Closing :: " + metaId);
-
-			RecordingChunk metaData = metaDataDao.get(metaId);
-			printMetaInfo(metaData, "Stopping the stream");
-			// Manually call finish on the stream so that there is no endless loop waiting in the RecordingConverter waiting for the stream to finish
-			// this would normally happen in the Listener
-			Status s = metaData.getStreamStatus();
-			switch (s) {
-				case NONE:
-					log.debug("Stream was not started, no need to stop :: stream with id {}", metaId);
-					break;
-				case STARTED:
-					metaData.setStreamStatus(listenerAdapter == null ? Status.STOPPED : Status.STOPPING);
-					metaDataDao.update(metaData);
-					break;
-				default:
-					//no-op
-					break;
-			}
-			if (listenerAdapter == null) {
-				log.debug("Stream Not Found :: {}", metaId);
-				log.debug("Available Streams :: {}", streamListeners.size());
-
-				for (Long entryKey : streamListeners.keySet()) {
-					log.debug("Stored recordingMetaDataId in Map: {}", entryKey);
-				}
-				throw new IllegalStateException("Could not find Listener to stop! metaId " + metaId);
-			}
-
-			listenerAdapter.closeStream();
-			streamListeners.remove(metaId);
-
-		} catch (Exception err) {
-			log.error("[removeListener]", err);
-		}
-	}
-
-	public void stopStreamRecord(IScope scope, StreamClient rcl) {
-		if (rcl == null || rcl.getMetaId() == null || rcl.getBroadcastId() == null) {
-			return;
-		}
-		log.debug("is this users still alive? stop it : {}", rcl);
-
-		removeListener(scope, rcl.getBroadcastId(), rcl.getMetaId());
-
-		// Update Meta Data
-		metaDataDao.updateEndDate(rcl.getMetaId(), new Date());
-
-		// Remove Meta Data
-		rcl.setMetaId(null);
-		streamClientManager.update(rcl);
-	}
-
-	public void startStreamRecord(IConnection conn) {
-		Long recordingId = IClientUtil.getRecordingId(conn.getScope());
-		Recording rec = recordingDao.get(recordingId);
-		startStreamRecord(conn, recordingId, rec.isInterview());
-	}
-
-	private void startStreamRecord(IConnection conn, Long recordingId, boolean isInterview) {
-		Date now = new Date();
-
-		StreamClient rcl = streamClientManager.get(IClientUtil.getId(conn.getClient()));
-		String broadcastId = rcl.getBroadcastId();
-		if (rcl.getMetaId() != null && streamListeners.get(rcl.getMetaId()) != null) {
-			log.debug("startStreamRecord[{}]:: existing metaId: {}", broadcastId, rcl.getMetaId());
-			return;
-		}
-
-		// If its the recording client we need another type of Meta Data
-		if (broadcastId != null) {
-			boolean audioOnly = "a".equals(rcl.getAvsettings());
-			boolean videoOnly = "v".equals(rcl.getAvsettings());
-			if (Client.Type.sharing == rcl.getType()) {
-				if (rcl.isSharingStarted() || rcl.isRecordingStarted()) {
-					String streamName = generateFileName(recordingId, broadcastId);
-
-					Long metaId = metaDataDao.add(recordingId, now, false, false, true, streamName, rcl.getSid());
-
-					// Start FLV Recording
-					addListener(conn, rcl.getBroadcastId(), streamName, metaId, true, isInterview);
-
-					// Add Meta Data
-					rcl.setMetaId(metaId);
-					streamClientManager.update(rcl);
-				}
-			} else if ("av".equals(rcl.getAvsettings()) || audioOnly || videoOnly) {
-				// if the user does publish av, a, v
-				// But we only record av or a, video only is not interesting
-				String streamName = generateFileName(recordingId, broadcastId);
-
-				Long metaId = metaDataDao.add(recordingId, now, audioOnly, videoOnly, false, streamName, rcl.getSid());
-
-				// Start FLV recording
-				addListener(conn, broadcastId, streamName, metaId, false, isInterview);
-
-				// Add Meta Data
-				rcl.setMetaId(metaId);
-				streamClientManager.update(rcl);
-			}
-		}
-		log.debug("startStreamRecord[{}]:: resulting metaId: {}", broadcastId, rcl.getMetaId());
-	}
-	*/
-}
diff --git a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/record/RecordingChunkDao.java b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/record/RecordingChunkDao.java
index a265b30..54a4be8 100644
--- a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/record/RecordingChunkDao.java
+++ b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/record/RecordingChunkDao.java
@@ -26,6 +26,7 @@ import javax.persistence.PersistenceContext;
 
 import org.apache.openmeetings.db.entity.record.RecordingChunk;
 import org.apache.openmeetings.db.entity.record.RecordingChunk.Status;
+import org.apache.openmeetings.db.entity.record.RecordingChunk.Type;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -56,52 +57,38 @@ public class RecordingChunkDao {
 	public List<RecordingChunk> getNotScreenChunksByRecording(Long recordingId) {
 		return em.createNamedQuery("getNotScreenChunkByRecording", RecordingChunk.class)
 				.setParameter("recordingId", recordingId)
+				.setParameter("screen", Type.SCREEN)
 				.setParameter("none", Status.NONE)
 				.getResultList();
 	}
 
 	public RecordingChunk getScreenByRecording(Long recordingId) {
 		List<RecordingChunk> list = em.createNamedQuery("getScreenChunkByRecording", RecordingChunk.class)
+				.setParameter("screen", Type.SCREEN)
 				.setParameter("recordingId", recordingId)
 				.getResultList();
 		return list.isEmpty() ? null : list.get(0);
 	}
 
-	public Long add(Long recordingId, Date recordStart, boolean isAudioOnly,
-			boolean isVideoOnly, boolean isScreenData, String streamName, String sid) {
-		try {
-
-			RecordingChunk chunk = new RecordingChunk();
-			chunk.setRecording(recordingDao.get(recordingId));
-			chunk.setRecordStart(recordStart);
-			chunk.setAudioOnly(isAudioOnly);
-			chunk.setVideoOnly(isVideoOnly);
-			chunk.setScreenData(isScreenData);
-			chunk.setStreamName(streamName);
-			chunk.setSid(sid);
-			chunk = update(chunk);
-			return chunk.getId();
-		} catch (Exception ex2) {
-			log.error("[add]: ", ex2);
-		}
-		return null;
+	public Long start(Long recordingId, Type type, String streamName, String sid) {
+		RecordingChunk chunk = new RecordingChunk();
+		chunk.setRecording(recordingDao.get(recordingId));
+		chunk.setStart(new Date());
+		chunk.setType(type);
+		chunk.setStreamName(streamName);
+		chunk.setStreamStatus(Status.STARTED);
+		chunk.setSid(sid);
+		chunk = update(chunk);
+		return chunk.getId();
 	}
 
-	public Long updateEndDate(Long chunkId, Date recordEnd) {
-		try {
-			RecordingChunk chunk = get(chunkId);
-
-			if (chunk != null) {
-				chunk.setRecordEnd(recordEnd);
-				log.debug("updateEndDate :: Start Date : {}", chunk.getRecordStart());
-				log.debug("updateEndDate :: End Date : {}", chunk.getRecordEnd());
-				update(chunk);
-			}
-			return chunkId;
-		} catch (Exception ex2) {
-			log.error("[updateEndDate]: ", ex2);
+	public void stop(Long chunkId) {
+		RecordingChunk chunk = get(chunkId);
+		if (chunk != null) {
+			chunk.setEnd(new Date());
+			chunk.setStreamStatus(Status.STOPPED);
+			update(chunk);
 		}
-		return null;
 	}
 
 	public RecordingChunk update(RecordingChunk chunk) {
diff --git a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/server/SOAPLoginDao.java b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/server/SOAPLoginDao.java
index 24228ab..e7b8e3c 100644
--- a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/server/SOAPLoginDao.java
+++ b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/server/SOAPLoginDao.java
@@ -18,9 +18,10 @@
  */
 package org.apache.openmeetings.db.dao.server;
 
+import static java.util.UUID.randomUUID;
+
 import java.util.Date;
 import java.util.List;
-import java.util.UUID;
 
 import javax.persistence.EntityManager;
 import javax.persistence.PersistenceContext;
@@ -48,7 +49,7 @@ public class SOAPLoginDao {
 		soapLogin.setUsed(false);
 		soapLogin.setRoomId(roomId);
 		soapLogin.setAllowSameURLMultipleTimes(allowSameURLMultipleTimes);
-		soapLogin.setHash(UUID.randomUUID().toString());
+		soapLogin.setHash(randomUUID().toString());
 		soapLogin.setRecordingId(recordingId);
 		soapLogin.setSessionHash(sessionHash);
 		soapLogin.setModerator(becomemoderator);
diff --git a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/server/SessiondataDao.java b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/server/SessiondataDao.java
index 762e9d3..5927049 100644
--- a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/server/SessiondataDao.java
+++ b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/server/SessiondataDao.java
@@ -18,10 +18,11 @@
  */
 package org.apache.openmeetings.db.dao.server;
 
+import static java.util.UUID.randomUUID;
+
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
-import java.util.UUID;
 
 import javax.persistence.EntityManager;
 import javax.persistence.PersistenceContext;
@@ -49,7 +50,7 @@ public class SessiondataDao {
 		log.debug("startsession :: startsession");
 
 		Sessiondata sd = new Sessiondata();
-		sd.setSessionId(UUID.randomUUID().toString());
+		sd.setSessionId(randomUUID().toString());
 		sd.setCreated(new Date());
 		sd.setUserId(null);
 
diff --git a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/user/UserDao.java b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/user/UserDao.java
index 7d303bd..6bf72c9 100644
--- a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/user/UserDao.java
+++ b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/user/UserDao.java
@@ -18,6 +18,7 @@
  */
 package org.apache.openmeetings.db.dao.user;
 
+import static java.util.UUID.randomUUID;
 import static org.apache.openmeetings.db.util.TimezoneUtil.getTimeZone;
 import static org.apache.openmeetings.util.DaoHelper.getStringParam;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.PARAM_USER_ID;
@@ -36,7 +37,6 @@ import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
-import java.util.UUID;
 
 import javax.persistence.EntityManager;
 import javax.persistence.PersistenceContext;
@@ -350,7 +350,7 @@ public class UserDao implements IGroupAdminDataProviderDao<User> {
 			u.setAddress(new Address());
 			u.setAge(new Date());
 			u.setExternalId(null);
-			final String purged = String.format("Purged %s", UUID.randomUUID());
+			final String purged = String.format("Purged %s", randomUUID());
 			u.setFirstname(purged);
 			u.setLastname(purged);
 			u.setLogin(purged);
@@ -542,7 +542,7 @@ public class UserDao implements IGroupAdminDataProviderDao<User> {
 			User to = new User();
 			to.setType(Type.contact);
 			String login = owner.getId() + "_" + email; //UserId prefix is used to ensure unique login
-			to.setLogin(login.length() < getMinLoginLength() ? UUID.randomUUID().toString() : login);
+			to.setLogin(login.length() < getMinLoginLength() ? randomUUID().toString() : login);
 			to.setFirstname(firstName);
 			to.setLastname(lastName);
 			to.setLanguageId(null == langId || null == LabelDao.getLocale(langId) ? owner.getLanguageId() : langId.longValue());
diff --git a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dto/room/InvitationDTO.java b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dto/room/InvitationDTO.java
index 7503567..322a1c9 100644
--- a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dto/room/InvitationDTO.java
+++ b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dto/room/InvitationDTO.java
@@ -18,10 +18,11 @@
  */
 package org.apache.openmeetings.db.dto.room;
 
+import static java.util.UUID.randomUUID;
+
 import java.io.Serializable;
 import java.text.ParseException;
 import java.util.Date;
-import java.util.UUID;
 
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
@@ -194,7 +195,7 @@ public class InvitationDTO implements Serializable {
 
 	public Invitation get(Long userId, UserDao userDao, RoomDao roomDao) {
 		Invitation i = new Invitation();
-		i.setHash(UUID.randomUUID().toString());
+		i.setHash(randomUUID().toString());
 		i.setPasswordProtected(passwordProtected);
 		if (passwordProtected) {
 			i.setPassword(CryptProvider.get().hash(password));
diff --git a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dto/room/Whiteboards.java b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dto/room/Whiteboards.java
index 1fb191d..228e27b 100644
--- a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dto/room/Whiteboards.java
+++ b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dto/room/Whiteboards.java
@@ -18,16 +18,17 @@
  */
 package org.apache.openmeetings.db.dto.room;
 
+import static java.util.UUID.randomUUID;
+
 import java.io.Serializable;
 import java.util.Map;
-import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicLong;
 
 public class Whiteboards implements Serializable {
 	private static final long serialVersionUID = 1L;
 	private Long roomId;
-	private final String uid = UUID.randomUUID().toString();
+	private final String uid = randomUUID().toString();
 	private Map<Long, Whiteboard> whiteboards = new ConcurrentHashMap<>();
 	private volatile AtomicLong whiteboardId = new AtomicLong(0);
 	private volatile AtomicLong activeWb = new AtomicLong(0);
diff --git a/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/basic/Client.java b/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/basic/Client.java
index 168e5f1..f1af803 100644
--- a/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/basic/Client.java
+++ b/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/basic/Client.java
@@ -18,6 +18,8 @@
  */
 package org.apache.openmeetings.db.entity.basic;
 
+import static java.util.UUID.randomUUID;
+
 import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -28,10 +30,10 @@ import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
-import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
 
 import org.apache.openmeetings.db.dao.user.UserDao;
+import org.apache.openmeetings.db.entity.IDataProviderEntity;
 import org.apache.openmeetings.db.entity.room.Room;
 import org.apache.openmeetings.db.entity.room.Room.Right;
 import org.apache.openmeetings.db.entity.user.User;
@@ -45,7 +47,7 @@ import com.github.openjson.JSONObject;
  * @author solomax
  *
  */
-public class Client implements IClient, IWsClient {
+public class Client implements IDataProviderEntity, IWsClient {
 	private static final long serialVersionUID = 1L;
 
 	public enum Activity {
@@ -53,8 +55,6 @@ public class Client implements IClient, IWsClient {
 		, broadcastV //sends Video to the room
 		, broadcastAV //sends Audio+Video to the room
 		, share
-		, record
-		, publish //sends A/V to external server
 	}
 	public enum StreamType {
 		webCam //sends Audio/Video to the room
@@ -76,15 +76,14 @@ public class Client implements IClient, IWsClient {
 	private int width = 0;
 	private int height = 0;
 	private String serverId = null;
-	private Long recordingId;
 
 	public Client(String sessionId, int pageId, Long userId, UserDao dao) {
 		this.sessionId = sessionId;
 		this.pageId = pageId;
 		this.user = dao.get(userId);
 		this.connectedSince = new Date();
-		uid = UUID.randomUUID().toString();
-		sid = UUID.randomUUID().toString();
+		uid = randomUUID().toString();
+		sid = randomUUID().toString();
 	}
 
 	@Override
@@ -106,32 +105,15 @@ public class Client implements IClient, IWsClient {
 		return this;
 	}
 
-	@Override
 	public Long getUserId() {
 		return user.getId();
 	}
 
 	@Override
-	public String getLogin() {
-		return user.getLogin();
-	}
-
-	@Override
-	public String getFirstname() {
-		return user.getFirstname();
-	}
-
-	@Override
-	public String getLastname() {
-		return user.getLastname();
-	}
-
-	@Override
 	public String getUid() {
 		return uid;
 	}
 
-	@Override
 	public String getSid() {
 		return sid;
 	}
@@ -297,7 +279,6 @@ public class Client implements IClient, IWsClient {
 		return this;
 	}
 
-	@Override
 	public int getWidth() {
 		return width;
 	}
@@ -307,7 +288,6 @@ public class Client implements IClient, IWsClient {
 		return this;
 	}
 
-	@Override
 	public int getHeight() {
 		return height;
 	}
@@ -317,7 +297,6 @@ public class Client implements IClient, IWsClient {
 		return this;
 	}
 
-	@Override
 	public String getRemoteAddress() {
 		return remoteAddress;
 	}
@@ -327,7 +306,6 @@ public class Client implements IClient, IWsClient {
 		return this;
 	}
 
-	@Override
 	public String getServerId() {
 		return serverId;
 	}
@@ -336,35 +314,10 @@ public class Client implements IClient, IWsClient {
 		this.serverId = serverId;
 	}
 
-	@Override
-	public void setRecordingStarted(boolean recordingStarted) {
-		if (recordingStarted) {
-			activities.add(Activity.record);
-		} else {
-			activities.remove(Activity.record);
-		}
-	}
-
-	@Override
-	public Long getRecordingId() {
-		return recordingId;
-	}
-
-	@Override
-	public void setRecordingId(Long recordingId) {
-		this.recordingId = recordingId;
-	}
-
-	@Override
 	public Long getRoomId() {
 		return room == null ? null : room.getId();
 	}
 
-	@Override
-	public Room.Type getRoomType() {
-		return room == null ? null : room.getType();
-	}
-
 	public JSONObject toJson(boolean self) {
 		JSONObject u = new JSONObject();
 		if (user != null) {
@@ -424,7 +377,6 @@ public class Client implements IClient, IWsClient {
 		mic = c.mic;
 		width = c.width;
 		height = c.height;
-		recordingId = c.recordingId;
 	}
 
 	@Override
@@ -480,7 +432,7 @@ public class Client implements IClient, IWsClient {
 		}
 
 		public StreamDesc(StreamType type) {
-			this.uuid = UUID.randomUUID().toString();
+			this.uuid = randomUUID().toString();
 			this.type = type;
 			setActivities();
 			if (StreamType.webCam == type) {
diff --git a/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/basic/IClient.java b/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/basic/IClient.java
deleted file mode 100644
index 59f6749..0000000
--- a/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/basic/IClient.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * 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.apache.openmeetings.db.entity.basic;
-
-import org.apache.openmeetings.db.entity.IDataProviderEntity;
-import org.apache.openmeetings.db.entity.room.Room;
-
-/**
- * Temporary interface, will be removed after 2 types of cliens will be merged
- * {@link org.apache.openmeetings.db.entity.room.StreamClient}
- * {@link org.apache.openmeetings.db.entity.basic.Client}
- * @author solomax
- *
- */
-public interface IClient extends IDataProviderEntity {
-	enum Type {
-		video
-		, room // room flash client
-		, sip
-		, mobile
-		, sharing
-	}
-	String getUid();
-	String getSid();
-	Long getUserId();
-	String getLogin();
-	String getFirstname();
-	String getLastname();
-	String getRemoteAddress();
-	Long getRoomId();
-	Room.Type getRoomType();
-	int getWidth();
-	int getHeight();
-	void setRecordingStarted(boolean recordingStarted);
-	Long getRecordingId();
-	void setRecordingId(Long recordingId);
-	String getServerId();
-}
diff --git a/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/basic/WsClient.java b/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/basic/WsClient.java
index 3107d46..a015b5b 100644
--- a/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/basic/WsClient.java
+++ b/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/basic/WsClient.java
@@ -18,7 +18,7 @@
  */
 package org.apache.openmeetings.db.entity.basic;
 
-import java.util.UUID;
+import static java.util.UUID.randomUUID;
 
 /**
  * Temporary class for test recording, will be unified
@@ -35,7 +35,7 @@ public class WsClient implements IWsClient {
 	public WsClient(String sessionId, int pageId) {
 		this.sessionId = sessionId;
 		this.pageId = pageId;
-		uid = UUID.randomUUID().toString();
+		uid = randomUUID().toString();
 	}
 	@Override
 	public String getSessionId() {
diff --git a/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/record/RecordingChunk.java b/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/record/RecordingChunk.java
index f6625d7..7a8c0dd 100644
--- a/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/record/RecordingChunk.java
+++ b/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/record/RecordingChunk.java
@@ -54,20 +54,27 @@ import org.simpleframework.xml.Root;
 	@NamedQuery(name = "getChunkById", query = "SELECT c FROM RecordingChunk c WHERE c.id = :id")
 	, @NamedQuery(name = "getChunkByRecording", query = "SELECT c FROM RecordingChunk c WHERE c.recording.id = :recordingId AND c.deleted = false")
 	, @NamedQuery(name = "getNotScreenChunkByRecording", query = "SELECT c FROM RecordingChunk c WHERE c.recording.id = :recordingId AND c.deleted = false "
-			+ "AND c.screenData = false AND c.streamStatus <> :none")
-	, @NamedQuery(name = "getScreenChunkByRecording", query = "SELECT c FROM RecordingChunk c WHERE c.recording.id = :recordingId AND c.screenData = true")
+			+ "AND c.type <> :screen AND c.streamStatus <> :none")
+	, @NamedQuery(name = "getScreenChunkByRecording", query = "SELECT c FROM RecordingChunk c WHERE c.recording.id = :recordingId AND c.type = :screen")
 })
 @Table(name = "recording_chunk")
 @Root(name = "flvrecordingmetadata")
 public class RecordingChunk extends HistoricalEntity {
 	private static final long serialVersionUID = 1L;
-	@XmlType(namespace="org.apache.openmeetings.record.meta")
+	@XmlType(namespace="org.apache.openmeetings.record.chunk")
 	public enum Status {
 		NONE
 		, STARTED
 		, STOPPING
 		, STOPPED
 	}
+	@XmlType(namespace="org.apache.openmeetings.record.chunk")
+	public enum Type {
+		AUDIO_ONLY
+		, VIDEO_ONLY
+		, AUDIO_VIDEO
+		, SCREEN
+	}
 
 	@Id
 	@GeneratedValue(strategy = GenerationType.IDENTITY)
@@ -80,13 +87,13 @@ public class RecordingChunk extends HistoricalEntity {
 	@ForeignKey(enabled = true)
 	private Recording recording;
 
-	@Column(name = "record_start")
-	@Element(data = true)
-	private Date recordStart;
+	@Column(name = "start")
+	@Element(name = "recordStart", data = true)
+	private Date start;
 
 	@Column(name = "record_end")
-	@Element(data = true, required = false)
-	private Date recordEnd;
+	@Element(name = "recordEnd", data = true, required = false)
+	private Date end;
 
 	@Column(name = "stream_name")
 	@Element(data = true)
@@ -96,30 +103,16 @@ public class RecordingChunk extends HistoricalEntity {
 	@Element(data = true, required = false)
 	private String sid;
 
-	@Column(name = "is_audio_only", nullable = false)
-	@Element(name = "isAudioOnly", data = true)
-	private boolean audioOnly;
-
-	@Column(name = "is_video_only", nullable = false)
-	@Element(name = "isVideoOnly", data = true)
-	private boolean videoOnly;
-
-	@Column(name = "is_screen_data", nullable = false)
-	@Element(name = "isScreenData", data = true)
-	private boolean screenData;
-
-	@Column(name = "full_wav_audio_data")
-	@Element(data = true, required = false)
-	private String fullWavAudioData;
-
-	@Column(name = "audio_is_valid", nullable = false)
-	@Element(name="audioIsValid", data = true, required = false)
-	private boolean audioValid;
+	@Column(name = "type")
+	@Element(data = true)
+	@Enumerated(EnumType.STRING)
+	private Type type;
 
 	/**
 	 * this is only STOPPED when the asynchronous stream writer's have completed to write packets to the file.
 	 */
 	@Column(name = "stream_status")
+	@Element(data = true)
 	@Enumerated(EnumType.STRING)
 	private Status streamStatus = Status.NONE;
 
@@ -141,44 +134,32 @@ public class RecordingChunk extends HistoricalEntity {
 		this.recording = recording;
 	}
 
-	public Date getRecordStart() {
-		return recordStart;
+	public Date getStart() {
+		return start;
 	}
 
-	public void setRecordStart(Date recordStart) {
-		this.recordStart = recordStart;
+	public void setStart(Date start) {
+		this.start = start;
 	}
 
-	public Date getRecordEnd() {
-		return recordEnd;
+	public Date getEnd() {
+		return end;
 	}
 
-	public void setRecordEnd(Date recordEnd) {
-		this.recordEnd = recordEnd;
-	}
-
-	public boolean isAudioOnly() {
-		return audioOnly;
+	public void setEnd(Date end) {
+		this.end = end;
 	}
 
-	public void setAudioOnly(boolean audioOnly) {
-		this.audioOnly = audioOnly;
+	public Type getType() {
+		return type;
 	}
 
-	public boolean isVideoOnly() {
-		return videoOnly;
+	public void setType(Type type) {
+		this.type = type;
 	}
 
-	public void setVideoOnly(boolean videoOnly) {
-		this.videoOnly = videoOnly;
-	}
-
-	public boolean isScreenData() {
-		return screenData;
-	}
-
-	public void setScreenData(boolean screenData) {
-		this.screenData = screenData;
+	public boolean isAudioOnly() {
+		return Type.AUDIO_ONLY == type;
 	}
 
 	public String getStreamName() {
@@ -197,22 +178,6 @@ public class RecordingChunk extends HistoricalEntity {
 		this.sid = sid;
 	}
 
-	public String getFullWavAudioData() {
-		return fullWavAudioData;
-	}
-
-	public void setFullWavAudioData(String fullWavAudioData) {
-		this.fullWavAudioData = fullWavAudioData;
-	}
-
-	public boolean isAudioValid() {
-		return audioValid;
-	}
-
-	public void setAudioValid(boolean audioValid) {
-		this.audioValid = audioValid;
-	}
-
 	public Status getStreamStatus() {
 		return streamStatus;
 	}
diff --git a/openmeetings-db/src/main/java/org/apache/openmeetings/db/manager/IClientManager.java b/openmeetings-db/src/main/java/org/apache/openmeetings/db/manager/IClientManager.java
index 5535ecc..17d3b22 100644
--- a/openmeetings-db/src/main/java/org/apache/openmeetings/db/manager/IClientManager.java
+++ b/openmeetings-db/src/main/java/org/apache/openmeetings/db/manager/IClientManager.java
@@ -23,7 +23,6 @@ import java.util.List;
 import java.util.Set;
 
 import org.apache.openmeetings.db.entity.basic.Client;
-import org.apache.openmeetings.db.entity.basic.IClient;
 
 public interface IClientManager {
 	Client get(String uid);
@@ -32,7 +31,7 @@ public interface IClientManager {
 	List<Client> listByRoom(Long roomId);
 	Collection<Client> listByUser(Long userId);
 	Client update(Client c);
-	void exit(IClient c);
+	void exit(Client c);
 
 
 	/**
diff --git a/openmeetings-db/src/main/java/org/apache/openmeetings/db/util/ws/RoomMessage.java b/openmeetings-db/src/main/java/org/apache/openmeetings/db/util/ws/RoomMessage.java
index b3b05cc..ec060b0 100644
--- a/openmeetings-db/src/main/java/org/apache/openmeetings/db/util/ws/RoomMessage.java
+++ b/openmeetings-db/src/main/java/org/apache/openmeetings/db/util/ws/RoomMessage.java
@@ -18,13 +18,13 @@
  */
 package org.apache.openmeetings.db.util.ws;
 
+import static java.util.UUID.randomUUID;
 import static org.apache.openmeetings.db.dao.room.SipDao.SIP_FIRST_NAME;
 import static org.apache.openmeetings.util.OmFileHelper.SIP_USER_ID;
 
 import java.util.Date;
-import java.util.UUID;
 
-import org.apache.openmeetings.db.entity.basic.IClient;
+import org.apache.openmeetings.db.entity.basic.Client;
 import org.apache.openmeetings.db.entity.user.User;
 import org.apache.wicket.protocol.ws.api.message.IWebSocketPushMessage;
 
@@ -36,8 +36,7 @@ public class RoomMessage implements IWebSocketPushMessage {
 		, roomClosed
 		, pollCreated
 		, pollUpdated
-		, recordingStarted
-		, recordingStoped
+		, recordingToggled
 		, sharingStarted
 		, sharingStoped
 		, rightUpdated
@@ -63,8 +62,8 @@ public class RoomMessage implements IWebSocketPushMessage {
 	private final String name;
 	private final Type type;
 
-	public RoomMessage(Long roomId, IClient c, Type type) {
-		this(roomId, c.getUserId(), c.getFirstname(), c.getLastname(), type);
+	public RoomMessage(Long roomId, Client c, Type type) {
+		this(roomId, c.getUser(), type);
 	}
 
 	public RoomMessage(Long roomId, User u, Type type) {
@@ -81,7 +80,7 @@ public class RoomMessage implements IWebSocketPushMessage {
 		}
 		this.userId = userId;
 		this.type = type;
-		this.uid = UUID.randomUUID().toString();
+		this.uid = randomUUID().toString();
 	}
 
 	public Date getTimestamp() {
diff --git a/openmeetings-db/src/main/java/org/apache/openmeetings/db/util/ws/TextRoomMessage.java b/openmeetings-db/src/main/java/org/apache/openmeetings/db/util/ws/TextRoomMessage.java
index 0750cd8..f2dbd77 100644
--- a/openmeetings-db/src/main/java/org/apache/openmeetings/db/util/ws/TextRoomMessage.java
+++ b/openmeetings-db/src/main/java/org/apache/openmeetings/db/util/ws/TextRoomMessage.java
@@ -18,14 +18,14 @@
  */
 package org.apache.openmeetings.db.util.ws;
 
-import org.apache.openmeetings.db.entity.basic.IClient;
+import org.apache.openmeetings.db.entity.basic.Client;
 import org.apache.openmeetings.db.entity.user.User;
 
 public class TextRoomMessage extends RoomMessage {
 	private static final long serialVersionUID = 1L;
 	private final String text;
 
-	public TextRoomMessage(Long roomId, IClient client, Type type, String text) {
+	public TextRoomMessage(Long roomId, Client client, Type type, String text) {
 		super(roomId, client, type);
 		this.text = text;
 	}
diff --git a/openmeetings-install/src/main/java/org/apache/openmeetings/backup/BackupImport.java b/openmeetings-install/src/main/java/org/apache/openmeetings/backup/BackupImport.java
index 724b672..060c2f3 100644
--- a/openmeetings-install/src/main/java/org/apache/openmeetings/backup/BackupImport.java
+++ b/openmeetings-install/src/main/java/org/apache/openmeetings/backup/BackupImport.java
@@ -18,13 +18,13 @@
  */
 package org.apache.openmeetings.backup;
 
+import static java.util.UUID.randomUUID;
 import static org.apache.openmeetings.db.entity.user.PrivateMessage.INBOX_FOLDER_ID;
 import static org.apache.openmeetings.db.entity.user.PrivateMessage.SENT_FOLDER_ID;
 import static org.apache.openmeetings.db.entity.user.PrivateMessage.TRASH_FOLDER_ID;
 import static org.apache.openmeetings.util.OmFileHelper.BCKP_RECORD_FILES;
 import static org.apache.openmeetings.util.OmFileHelper.BCKP_ROOM_FILES;
 import static org.apache.openmeetings.util.OmFileHelper.CSS_DIR;
-import static org.apache.openmeetings.util.OmFileHelper.EXTENSION_FLV;
 import static org.apache.openmeetings.util.OmFileHelper.EXTENSION_JPG;
 import static org.apache.openmeetings.util.OmFileHelper.EXTENSION_MP4;
 import static org.apache.openmeetings.util.OmFileHelper.EXTENSION_PNG;
@@ -108,7 +108,6 @@ import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.UUID;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipInputStream;
 
@@ -555,14 +554,14 @@ public class BackupImport {
 			if (u.getAddress() != null && u.getAddress().getEmail() != null && User.Type.user == u.getType()) {
 				if (userEmailMap.containsKey(u.getAddress().getEmail())) {
 					log.warn("Email is duplicated for user " + u.toString());
-					String updateEmail = String.format("modified_by_import_<%s>%s", UUID.randomUUID(), u.getAddress().getEmail());
+					String updateEmail = String.format("modified_by_import_<%s>%s", randomUUID(), u.getAddress().getEmail());
 					u.getAddress().setEmail(updateEmail);
 				}
 				userEmailMap.put(u.getAddress().getEmail(), Integer.valueOf(userEmailMap.size()));
 			}
 			if (userLoginMap.containsKey(u.getLogin())) {
 				log.warn("Login is duplicated for user " + u.toString());
-				String updateLogin = String.format("modified_by_import_<%s>%s", UUID.randomUUID(), u.getLogin());
+				String updateLogin = String.format("modified_by_import_<%s>%s", randomUUID(), u.getLogin());
 				u.setLogin(updateLogin);
 			}
 			userLoginMap.put(u.getLogin(), Integer.valueOf(userLoginMap.size()));
@@ -572,7 +571,7 @@ public class BackupImport {
 				}
 			}
 			if (u.getType() == User.Type.contact && u.getLogin().length() < minLoginLength) {
-				u.setLogin(UUID.randomUUID().toString());
+				u.setLogin(randomUUID().toString());
 			}
 
 			String tz = u.getTimeZoneId();
@@ -799,12 +798,12 @@ public class BackupImport {
 			}
 			if (!Strings.isEmpty(r.getHash()) && r.getHash().startsWith(RECORDING_FILE_NAME)) {
 				String name = getFileName(r.getHash());
-				r.setHash(UUID.randomUUID().toString());
+				r.setHash(randomUUID().toString());
 				fileMap.put(String.format(FILE_NAME_FMT, name, EXTENSION_JPG), String.format(FILE_NAME_FMT, r.getHash(), EXTENSION_PNG));
-				fileMap.put(String.format("%s.%s.%s", name, EXTENSION_FLV, EXTENSION_MP4), String.format(FILE_NAME_FMT, r.getHash(), EXTENSION_MP4));
+				fileMap.put(String.format("%s.%s.%s", name, "flv", EXTENSION_MP4), String.format(FILE_NAME_FMT, r.getHash(), EXTENSION_MP4));
 			}
 			if (Strings.isEmpty(r.getHash())) {
-				r.setHash(UUID.randomUUID().toString());
+				r.setHash(randomUUID().toString());
 			}
 			r = recordingDao.update(r);
 			fileItemMap.put(recId, r.getId());
@@ -931,7 +930,7 @@ public class BackupImport {
 				file.setParentId(null);
 			}
 			if (Strings.isEmpty(file.getHash())) {
-				file.setHash(UUID.randomUUID().toString());
+				file.setHash(randomUUID().toString());
 			}
 			file = fileItemDao.update(file);
 			result.add(file);
diff --git a/openmeetings-install/src/main/java/org/apache/openmeetings/backup/converter/WbConverter.java b/openmeetings-install/src/main/java/org/apache/openmeetings/backup/converter/WbConverter.java
index 16196b1..983bba5 100644
--- a/openmeetings-install/src/main/java/org/apache/openmeetings/backup/converter/WbConverter.java
+++ b/openmeetings-install/src/main/java/org/apache/openmeetings/backup/converter/WbConverter.java
@@ -19,6 +19,7 @@
 package org.apache.openmeetings.backup.converter;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.UUID.randomUUID;
 import static org.apache.commons.lang3.math.NumberUtils.toLong;
 import static org.apache.openmeetings.db.dto.room.Whiteboard.ATTR_FILE_ID;
 import static org.apache.openmeetings.db.dto.room.Whiteboard.ATTR_FILE_TYPE;
@@ -37,7 +38,6 @@ import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
-import java.util.UUID;
 
 import org.apache.commons.lang.math.NumberUtils;
 import org.apache.openmeetings.db.dto.room.Whiteboard;
@@ -70,7 +70,7 @@ public class WbConverter {
 	}
 
 	private static void add(Whiteboard wb, JSONObject o) {
-		String uid = UUID.randomUUID().toString();
+		String uid = randomUUID().toString();
 		wb.put(uid, o.put("uid", uid));
 	}
 
diff --git a/openmeetings-screenshare/src/main/java/org/apache/openmeetings/screenshare/Core.java b/openmeetings-screenshare/src/main/java/org/apache/openmeetings/screenshare/Core.java
index 3358092..fa13e13 100644
--- a/openmeetings-screenshare/src/main/java/org/apache/openmeetings/screenshare/Core.java
+++ b/openmeetings-screenshare/src/main/java/org/apache/openmeetings/screenshare/Core.java
@@ -19,6 +19,7 @@
 package org.apache.openmeetings.screenshare;
 
 import static java.lang.Boolean.TRUE;
+import static java.util.UUID.randomUUID;
 import static org.apache.openmeetings.screenshare.util.Util.getQurtzProps;
 import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
 import static org.slf4j.LoggerFactory.getLogger;
@@ -29,7 +30,6 @@ import java.net.ConnectException;
 import java.net.URI;
 import java.util.HashMap;
 import java.util.Map;
-import java.util.UUID;
 import java.util.concurrent.LinkedBlockingQueue;
 
 import org.apache.openmeetings.screenshare.gui.ScreenDimensions;
@@ -542,7 +542,7 @@ public class Core implements IPendingServiceCallback, INetStreamEventHandler {
 						}
 						capture.setStreamId((Number)o);
 					}
-					final String broadcastId = UUID.randomUUID().toString();
+					final String broadcastId = randomUUID().toString();
 					log.debug("createPublishStream result stream id: {}; name: {}", capture.getStreamId(), broadcastId);
 					instance.publish(capture.getStreamId(), broadcastId, "live", this);
 
diff --git a/openmeetings-service/src/main/java/org/apache/openmeetings/service/calendar/caldav/IcalUtils.java b/openmeetings-service/src/main/java/org/apache/openmeetings/service/calendar/caldav/IcalUtils.java
index 392b40a..738e7c9 100644
--- a/openmeetings-service/src/main/java/org/apache/openmeetings/service/calendar/caldav/IcalUtils.java
+++ b/openmeetings-service/src/main/java/org/apache/openmeetings/service/calendar/caldav/IcalUtils.java
@@ -18,6 +18,7 @@
  */
 package org.apache.openmeetings.service.calendar.caldav;
 
+import static java.util.UUID.randomUUID;
 import static org.apache.openmeetings.db.util.TimezoneUtil.getTimeZone;
 
 import java.net.URI;
@@ -412,7 +413,7 @@ public class IcalUtils {
 		String uid = appointment.getIcalId();
 		Uid ui;
 		if (uid == null || uid.length() < 1) {
-			UUID uuid = UUID.randomUUID();
+			UUID uuid = randomUUID();
 			appointment.setIcalId(uuid.toString());
 			ui = new Uid(uuid.toString());
 		} else {
diff --git a/openmeetings-service/src/main/java/org/apache/openmeetings/service/room/InvitationManager.java b/openmeetings-service/src/main/java/org/apache/openmeetings/service/room/InvitationManager.java
index 0a36b8d..839d312 100644
--- a/openmeetings-service/src/main/java/org/apache/openmeetings/service/room/InvitationManager.java
+++ b/openmeetings-service/src/main/java/org/apache/openmeetings/service/room/InvitationManager.java
@@ -18,6 +18,7 @@
  */
 package org.apache.openmeetings.service.room;
 
+import static java.util.UUID.randomUUID;
 import static org.apache.openmeetings.db.util.ApplicationHelper.ensureApplication;
 import static org.apache.openmeetings.db.util.TimezoneUtil.getTimeZone;
 
@@ -26,7 +27,6 @@ import java.util.Date;
 import java.util.List;
 import java.util.Map;
 import java.util.TimeZone;
-import java.util.UUID;
 
 import org.apache.openmeetings.IApplication;
 import org.apache.openmeetings.core.mail.MailHandler;
@@ -190,7 +190,7 @@ public class InvitationManager implements IInvitationManager {
 		Invitation invitation = _invitation;
 		if (null == _invitation) {
 			invitation = new Invitation();
-			invitation.setHash(UUID.randomUUID().toString());
+			invitation.setHash(randomUUID().toString());
 		}
 
 		invitation.setPasswordProtected(isPasswordProtected);
diff --git a/openmeetings-util/src/main/java/org/apache/openmeetings/util/OmFileHelper.java b/openmeetings-util/src/main/java/org/apache/openmeetings/util/OmFileHelper.java
index 2266295..de7508b 100644
--- a/openmeetings-util/src/main/java/org/apache/openmeetings/util/OmFileHelper.java
+++ b/openmeetings-util/src/main/java/org/apache/openmeetings/util/OmFileHelper.java
@@ -23,8 +23,11 @@ import java.io.IOException;
 import java.text.DecimalFormat;
 
 import org.apache.openmeetings.util.ConnectionProperties.DbType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class OmFileHelper {
+	private static final Logger log = LoggerFactory.getLogger(OmFileHelper.class);
 	/**
 	 * This variable needs to point to the openmeetings webapp directory
 	 */
@@ -63,7 +66,7 @@ public class OmFileHelper {
 	public static final String TEST_SETUP_PREFIX = "TEST_SETUP_";
 	public static final String DASHBOARD_FILE = "dashboard.xml";
 	public static final String EXTENSION_WML = "wml";
-	public static final String EXTENSION_FLV = "flv";
+	public static final String EXTENSION_WEBM = "webm";
 	public static final String EXTENSION_MP4 = "mp4";
 	public static final String EXTENSION_JPG = "jpg";
 	public static final String EXTENSION_PNG = "png";
@@ -199,12 +202,18 @@ public class OmFileHelper {
 		return getDir(getStreamsDir(), name);
 	}
 
+	public static String getRecUri(File f) throws IOException {
+		String uri = f.getCanonicalFile().toURI().toString();
+		log.info("Configured to record to {}", uri);
+		return uri;
+	}
+
 	public static String getName(String name, String ext) {
 		return String.format(FILE_NAME_FMT, name, ext);
 	}
 
 	public static File getRecordingChunk(Long roomId, String name) {
-		return new File(getStreamsSubDir(roomId), getName(name, EXTENSION_FLV));
+		return new File(getStreamsSubDir(roomId), getName(name, EXTENSION_WEBM));
 	}
 
 	public static File getLanguagesDir() {
diff --git a/openmeetings-util/src/main/java/org/apache/openmeetings/util/mail/IcalHandler.java b/openmeetings-util/src/main/java/org/apache/openmeetings/util/mail/IcalHandler.java
index f147a35..9805849 100644
--- a/openmeetings-util/src/main/java/org/apache/openmeetings/util/mail/IcalHandler.java
+++ b/openmeetings-util/src/main/java/org/apache/openmeetings/util/mail/IcalHandler.java
@@ -18,6 +18,8 @@
  */
 package org.apache.openmeetings.util.mail;
 
+import static java.util.UUID.randomUUID;
+
 import java.io.ByteArrayOutputStream;
 import java.io.FileOutputStream;
 import java.net.URI;
@@ -26,7 +28,6 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
-import java.util.UUID;
 
 import org.apache.wicket.util.string.Strings;
 import org.slf4j.Logger;
@@ -136,7 +137,7 @@ public class IcalHandler {
 		// generate unique identifier (if not submitted)
 		Uid ui;
 		if (Strings.isEmpty(uid)) {
-			ui = new Uid(UUID.randomUUID().toString());
+			ui = new Uid(randomUUID().toString());
 			log.debug("Generating Meeting UID : " + ui.getValue());
 		} else {
 			ui = new Uid(uid);
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/connection/ConnectionsPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/connection/ConnectionsPanel.java
index 56f089e..95e49f1 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/connection/ConnectionsPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/connection/ConnectionsPanel.java
@@ -29,7 +29,6 @@ import java.util.List;
 import org.apache.openmeetings.core.remote.KurentoHandler;
 import org.apache.openmeetings.db.dao.user.IUserManager;
 import org.apache.openmeetings.db.entity.basic.Client;
-import org.apache.openmeetings.db.entity.basic.IClient;
 import org.apache.openmeetings.web.admin.AdminBasePanel;
 import org.apache.openmeetings.web.admin.SearchableDataView;
 import org.apache.openmeetings.web.app.ClientManager;
@@ -59,19 +58,18 @@ public class ConnectionsPanel extends AdminBasePanel {
 	public ConnectionsPanel(String id) {
 		super(id);
 
-		SearchableDataProvider<IClient> sdp = new SearchableDataProvider<IClient>(null) {
+		SearchableDataProvider<Client> sdp = new SearchableDataProvider<Client>(null) {
 			private static final long serialVersionUID = 1L;
 
-			private List<IClient> list() {
-				List<IClient> l = new ArrayList<>();
-				//l.addAll(scm.list());
+			private List<Client> list() {
+				List<Client> l = new ArrayList<>();
 				l.addAll(cm.list());
 				return l;
 			}
 
 			@Override
-			public Iterator<? extends IClient> iterator(long first, long count) {
-				List<IClient> l = list();
+			public Iterator<? extends Client> iterator(long first, long count) {
+				List<Client> l = list();
 				return l.subList((int)Math.max(0, first), (int)Math.min(first + count, l.size())).iterator();
 			}
 
@@ -82,43 +80,27 @@ public class ConnectionsPanel extends AdminBasePanel {
 		};
 		final WebMarkupContainer container = new WebMarkupContainer("container");
 		final WebMarkupContainer details = new WebMarkupContainer("details");
-		SearchableDataView<IClient> dataView = new SearchableDataView<IClient>("clientList", sdp) {
+		SearchableDataView<Client> dataView = new SearchableDataView<Client>("clientList", sdp) {
 			private static final long serialVersionUID = 1L;
 
 			@Override
-			protected void populateItem(final Item<IClient> item) {
-				IClient _c = item.getModelObject();
+			protected void populateItem(final Item<Client> item) {
+				Client c = item.getModelObject();
 				final ConfirmableAjaxBorder confirm = new ConfirmableAjaxBorder("kick", getString("603"), getString("605")) {
 					private static final long serialVersionUID = 1L;
 
 					@Override
 					protected void onSubmit(AjaxRequestTarget target) {
-						IClient _c = item.getModelObject();
-						/*FIXME TODO if (_c instanceof StreamClient) {
-							userManager.kickById(_c.getUid());
-						} else */{
-							Client c = (Client)_c;
-							cm.invalidate(c.getUserId(), c.getSessionId());
-						}
+						cm.invalidate(c.getUserId(), c.getSessionId());
 						target.add(container, details.setVisible(false));
 					}
 				};
 				confirm.setOutputMarkupId(true).add(new ButtonBehavior(String.format("#%s", confirm.getMarkupId())));
-				/*FIXME TODO if (_c instanceof StreamClient) {
-					StreamClient c = (StreamClient)_c;
-					item.add(new Label("type", "flash"));
-					item.add(new Label("login", c.getLogin()));
-					item.add(new Label("since", c.getConnectedSince()));
-					item.add(new Label("scope"));
-					confirm.setEnabled(Client.Type.sharing != c.getType());
-				} else */{
-					Client c = (Client)_c;
-					item.add(new Label("type", "html5"));
-					item.add(new Label("login", c.getUser().getLogin()));
-					item.add(new Label("since", c.getConnectedSince()));
-					item.add(new Label("scope", c.getRoom() == null ? "html5" : "" + c.getRoom().getId()));
-				}
-				item.add(new Label("server", _c.getServerId()));
+				item.add(new Label("type", "html5"));
+				item.add(new Label("login", c.getUser().getLogin()));
+				item.add(new Label("since", c.getConnectedSince()));
+				item.add(new Label("scope", c.getRoom() == null ? "html5" : "" + c.getRoom().getId()));
+				item.add(new Label("server", c.getServerId()));
 				item.add(confirm);
 				item.add(new AjaxEventBehavior(EVT_CLICK) {
 					private static final long serialVersionUID = 1L;
@@ -127,7 +109,7 @@ public class ConnectionsPanel extends AdminBasePanel {
 					protected void onEvent(AjaxRequestTarget target) {
 						Field[] ff = item.getModelObject().getClass().getDeclaredFields();
 						RepeatingView lines = new RepeatingView("line");
-						IClient c = item.getModelObject();
+						Client c = item.getModelObject();
 						for (Field f : ff) {
 							int mod = f.getModifiers();
 							if (Modifier.isStatic(mod) || Modifier.isTransient(mod)) {
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/rooms/RoomForm.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/rooms/RoomForm.java
index e97fb3d..8554897 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/rooms/RoomForm.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/rooms/RoomForm.java
@@ -96,9 +96,9 @@ public class RoomForm extends AdminBaseForm<Room> {
 
 		@Override
 		protected void populateItem(final ListItem<Client> item) {
-			Client client = item.getModelObject();
-			item.add(new Label("clientId", "" + client.getUserId()))
-				.add(new Label("clientLogin", "" + client.getLogin()))
+			Client c = item.getModelObject();
+			item.add(new Label("clientId", "" + c.getUserId()))
+				.add(new Label("clientLogin", "" + c.getUser().getLogin()))
 				.add(new ConfirmableAjaxBorder("clientDelete", getString("80"), getString("833")) {
 					private static final long serialVersionUID = 1L;
 
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/users/UserForm.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/users/UserForm.java
index 92934e0..f083918 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/users/UserForm.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/users/UserForm.java
@@ -18,6 +18,7 @@
  */
 package org.apache.openmeetings.web.admin.users;
 
+import static java.util.UUID.randomUUID;
 import static org.apache.openmeetings.db.dao.user.UserDao.getNewUserInstance;
 import static org.apache.openmeetings.db.util.AuthLevelUtil.hasAdminLevel;
 import static org.apache.openmeetings.db.util.AuthLevelUtil.hasGroupAdminLevel;
@@ -33,7 +34,6 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.UUID;
 
 import org.apache.openmeetings.core.util.StrongPasswordValidator;
 import org.apache.openmeetings.db.dao.server.LdapConfigDao;
@@ -242,7 +242,7 @@ public class UserForm extends AdminBaseForm<User> {
 		final boolean isNew = u.getId() == null;
 		boolean sendEmailAtRegister = isSendRegisterEmail();
 		if (isNew && sendEmailAtRegister) {
-			u.setActivatehash(UUID.randomUUID().toString());
+			u.setActivatehash(randomUUID().toString());
 		}
 		try {
 			u = userDao.update(u, pass, getUserId());
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/ClientManager.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/ClientManager.java
index 4a00a46..3b69309 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/ClientManager.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/ClientManager.java
@@ -35,7 +35,6 @@ import javax.annotation.PostConstruct;
 import org.apache.openmeetings.core.remote.KurentoHandler;
 import org.apache.openmeetings.db.dao.log.ConferenceLogDao;
 import org.apache.openmeetings.db.entity.basic.Client;
-import org.apache.openmeetings.db.entity.basic.IClient;
 import org.apache.openmeetings.db.entity.log.ConferenceLog;
 import org.apache.openmeetings.db.manager.IClientManager;
 import org.apache.openmeetings.db.util.ws.RoomMessage;
@@ -125,7 +124,7 @@ public class ClientManager implements IClientManager {
 		return mapBySid().get(sid);
 	}
 
-	public void exitRoom(IClient c) {
+	public void exitRoom(Client c) {
 		Long roomId = c.getRoomId();
 		removeFromRoom(c);
 		if (roomId != null) {
@@ -139,10 +138,10 @@ public class ClientManager implements IClientManager {
 	}
 
 	@Override
-	public void exit(IClient c) {
+	public void exit(Client c) {
 		if (c != null) {
 			exitRoom(c);
-			kHandler.remove((Client)c);
+			kHandler.remove(c);
 			log.debug("Removing online client: {}, roomId: {}", c.getUid(), c.getRoomId());
 			map().remove(c.getUid());
 			onlineClients.remove(c.getUid());
@@ -186,28 +185,25 @@ public class ClientManager implements IClientManager {
 		return count;
 	}
 
-	public IClient removeFromRoom(IClient _c) {
-		Long roomId = _c.getRoomId();
-		log.debug("Removing online room client: {}, room: {}", _c.getUid(), roomId);
+	public Client removeFromRoom(Client c) {
+		Long roomId = c.getRoomId();
+		log.debug("Removing online room client: {}, room: {}", c.getUid(), roomId);
 		if (roomId != null) {
 			IMap<Long, Set<String>> rooms = rooms();
 			rooms.lock(roomId);
 			Set<String> clients = rooms.get(roomId);
 			if (clients != null) {
-				clients.remove(_c.getUid());
+				clients.remove(c.getUid());
 				rooms.put(roomId, clients);
 				onlineRooms.put(roomId, clients);
 			}
 			rooms.unlock(roomId);
-			if (_c instanceof Client) {
-				Client c = (Client)_c;
-				kHandler.leaveRoom(c);
-				c.setRoom(null);
-				c.clear();
-				update(c);
-			}
+			kHandler.leaveRoom(c);
+			c.setRoom(null);
+			c.clear();
+			update(c);
 		}
-		return _c;
+		return c;
 	}
 
 	public boolean isOnline(Long userId) {
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/UserManager.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/UserManager.java
index 248faba..600cef5 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/UserManager.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/UserManager.java
@@ -18,6 +18,7 @@
  */
 package org.apache.openmeetings.web.app;
 
+import static java.util.UUID.randomUUID;
 import static org.apache.openmeetings.db.dao.user.UserDao.getNewUserInstance;
 import static org.apache.openmeetings.db.util.TimezoneUtil.getTimeZone;
 import static org.apache.openmeetings.util.OmException.UNKNOWN;
@@ -32,7 +33,6 @@ import java.io.IOException;
 import java.security.NoSuchAlgorithmException;
 import java.util.Date;
 import java.util.Locale;
-import java.util.UUID;
 
 import org.apache.openmeetings.db.dao.label.LabelDao;
 import org.apache.openmeetings.db.dao.user.GroupDao;
@@ -144,7 +144,7 @@ public class UserManager implements IUserManager {
 			String email = u.getAddress() == null ? null : u.getAddress().getEmail();
 			boolean checkEmail = Strings.isEmpty(email) || userDao.checkEmail(email, User.Type.user, null, null);
 			if (checkName && checkEmail) {
-				String ahash = Strings.isEmpty(hash) ? UUID.randomUUID().toString() : hash;
+				String ahash = Strings.isEmpty(hash) ? randomUUID().toString() : hash;
 				if (Strings.isEmpty(u.getExternalType())) {
 					if (!Strings.isEmpty(email)) {
 						emailManager.sendMail(login, email, ahash, sendConfirmation(), u.getLanguageId());
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/InvitationForm.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/InvitationForm.java
index 9ec6417..ea48898 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/InvitationForm.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/InvitationForm.java
@@ -18,6 +18,7 @@
  */
 package org.apache.openmeetings.web.common;
 
+import static java.util.UUID.randomUUID;
 import static org.apache.openmeetings.util.CalendarHelper.getDate;
 import static org.apache.openmeetings.web.app.Application.getInvitationLink;
 import static org.apache.openmeetings.web.app.WebSession.AVAILABLE_TIMEZONES;
@@ -26,7 +27,6 @@ import static org.apache.openmeetings.web.app.WebSession.getUserId;
 import java.time.LocalDateTime;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.UUID;
 
 import org.apache.openmeetings.db.dao.room.InvitationDao;
 import org.apache.openmeetings.db.dao.user.UserDao;
@@ -167,7 +167,7 @@ public abstract class InvitationForm extends Form<Invitation> {
 		i.setValidTo(getDate(to.getModelObject(), timeZoneId.getModelObject()));
 
 		i.setInvitee(u);
-		i.setHash(UUID.randomUUID().toString());
+		i.setHash(randomUUID().toString());
 		if (Type.contact == u.getType()) {
 			u.setLanguageId(lang.getModelObject());
 		}
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/tree/FileTreePanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/tree/FileTreePanel.java
index b3beb83..70a8992 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/tree/FileTreePanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/tree/FileTreePanel.java
@@ -18,6 +18,7 @@
  */
 package org.apache.openmeetings.web.common.tree;
 
+import static java.util.UUID.randomUUID;
 import static org.apache.commons.text.StringEscapeUtils.escapeEcmaScript;
 import static org.apache.openmeetings.util.OmFileHelper.EXTENSION_JPG;
 import static org.apache.openmeetings.util.OmFileHelper.EXTENSION_PDF;
@@ -34,7 +35,6 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
-import java.util.UUID;
 
 import org.apache.openmeetings.db.dao.file.FileItemDao;
 import org.apache.openmeetings.db.dao.record.RecordingDao;
@@ -175,7 +175,7 @@ public abstract class FileTreePanel extends Panel {
 
 							@Override
 							public CharSequence getCallbackFunctionBody(CallbackParameter... parameters) {
-								String dialogId = UUID.randomUUID().toString();
+								String dialogId = randomUUID().toString();
 
 								String statement = "var $drop = $(this);";
 								statement += "$('body').append('<div id=" + dialogId + ">" + getString("713") + "</div>');";
@@ -303,7 +303,7 @@ public abstract class FileTreePanel extends Panel {
 		boolean isRecording = p instanceof Recording;
 		BaseFileItem f = isRecording ? new Recording() : new FileItem();
 		f.setName(name);
-		f.setHash(UUID.randomUUID().toString());
+		f.setHash(randomUUID().toString());
 		f.setInsertedBy(getUserId());
 		f.setInserted(new Date());
 		f.setType(Type.Folder);
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/ForgetPasswordDialog.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/ForgetPasswordDialog.java
index 24896e3..c3e9ed4 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/ForgetPasswordDialog.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/ForgetPasswordDialog.java
@@ -18,6 +18,7 @@
  */
 package org.apache.openmeetings.web.pages.auth;
 
+import static java.util.UUID.randomUUID;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.getBaseUrl;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.getMinLoginLength;
 import static org.apache.openmeetings.web.app.Application.urlForPage;
@@ -25,7 +26,6 @@ import static org.apache.openmeetings.web.app.Application.urlForPage;
 import java.util.Arrays;
 import java.util.Date;
 import java.util.List;
-import java.util.UUID;
 
 import org.apache.openmeetings.core.mail.MailHandler;
 import org.apache.openmeetings.db.dao.user.UserDao;
@@ -259,7 +259,7 @@ public class ForgetPasswordDialog extends AbstractFormDialog<String> {
 
 	private void sendHashByUser(User us) {
 		log.debug("User: " + us.getLogin());
-		us.setResethash(UUID.randomUUID().toString());
+		us.setResethash(randomUUID().toString());
 		us.setResetDate(new Date());
 		userDao.update(us, null);
 		String resetLink = urlForPage(ResetPage.class
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.java
index 8fcdca7..7c43b14 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.java
@@ -175,7 +175,7 @@ public class RoomPanel extends BasePanel {
 					sb.append("VideoManager.play(").append(streams).append(", ").append(kHandler.getTurnServers()).append(");");
 				}
 			}
-			if (interview && recordingUser == null && streams.length() > 0 && _c.hasRight(Right.moderator)) {
+			if (interview && !kHandler.isRecording(r.getId()) && streams.length() > 0 && _c.hasRight(Right.moderator)) {
 				sb.append("WbArea.setRecEnabled(true);");
 			}
 			if (!Strings.isEmpty(sb)) {
@@ -190,7 +190,6 @@ public class RoomPanel extends BasePanel {
 	private RoomSidebar sidebar;
 	private final AbstractWbPanel wb;
 	private String sharingUser = null;
-	private String recordingUser = null;
 	private byte[] pdfWb;
 	private final AjaxDownloadBehavior download = new AjaxDownloadBehavior(new ResourceStreamResource() {
 		private static final long serialVersionUID = 1L;
@@ -423,34 +422,9 @@ public class RoomPanel extends BasePanel {
 					case pollUpdated:
 						menu.updatePoll(handler, null);
 						break;
-					case recordingStoped:
-						{
-							String uid = ((TextRoomMessage)m).getText();
-							Client c = cm.getBySid(uid);
-							if (c == null) {
-								log.error("Not existing/BAD user has stopped recording {} != {} !!!!", uid);
-								return;
-							}
-							recordingUser = null;
-							cm.update(c.remove(Client.Activity.record));
-							menu.update(handler);
-							updateInterviewRecordingButtons(handler);
-						}
-						break;
-					case recordingStarted:
-						{
-							JSONObject obj = new JSONObject(((TextRoomMessage)m).getText());
-							String sid = obj.getString("sid");
-							Client c = cm.getBySid(sid);
-							if (c == null) {
-								log.error("Not existing user has started recording {} !!!!", sid);
-								return;
-							}
-							recordingUser = sid;
-							cm.update(c.set(Client.Activity.record));
-							menu.update(handler);
-							updateInterviewRecordingButtons(handler);
-						}
+					case recordingToggled:
+						menu.update(handler);
+						updateInterviewRecordingButtons(handler);
 						break;
 					case sharingStoped:
 						{
@@ -595,7 +569,9 @@ public class RoomPanel extends BasePanel {
 	private void updateInterviewRecordingButtons(IPartialPageRequestHandler handler) {
 		Client _c = getClient();
 		if (interview && _c.hasRight(Right.moderator)) {
-			if (recordingUser == null) {
+			if (kHandler.isRecording(r.getId())) {
+				handler.appendJavaScript("if (typeof(WbArea) === 'object') {WbArea.setRecStarted(true);}");
+			} else {
 				boolean hasStreams = false;
 				for (Client cl : cm.listByRoom(r.getId())) {
 					if (!cl.getStreams().isEmpty()) {
@@ -604,8 +580,6 @@ public class RoomPanel extends BasePanel {
 					}
 				}
 				handler.appendJavaScript(String.format("if (typeof(WbArea) === 'object') {WbArea.setRecStarted(false);WbArea.setRecEnabled(%s);}", hasStreams));
-			} else {
-				handler.appendJavaScript("if (typeof(WbArea) === 'object') {WbArea.setRecStarted(true);}");
 			}
 		}
 	}
@@ -807,10 +781,6 @@ public class RoomPanel extends BasePanel {
 		return sharingUser;
 	}
 
-	public String getRecordingUser() {
-		return recordingUser;
-	}
-
 	public String getPublishingUser() {
 		return null;
 	}
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/menu/RoomMenuPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/menu/RoomMenuPanel.java
index 3634469..26e9641 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/menu/RoomMenuPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/menu/RoomMenuPanel.java
@@ -26,9 +26,11 @@ import static org.apache.openmeetings.web.util.GroupLogoResourceReference.getUrl
 import static org.apache.openmeetings.web.util.OmUrlFragment.ROOMS_PUBLIC;
 
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.List;
 
 import org.apache.commons.lang3.time.FastDateFormat;
+import org.apache.openmeetings.core.remote.KurentoHandler;
 import org.apache.openmeetings.core.util.WebSocketHelper;
 import org.apache.openmeetings.db.dao.basic.ConfigurationDao;
 import org.apache.openmeetings.db.entity.basic.Client;
@@ -57,6 +59,7 @@ import org.apache.wicket.request.flow.RedirectToUrlException;
 import org.apache.wicket.spring.injection.annot.SpringBean;
 import org.apache.wicket.util.string.Strings;
 
+import com.github.openjson.JSONObject;
 import com.googlecode.wicket.jquery.ui.widget.menu.IMenuItem;
 
 public class RoomMenuPanel extends Panel {
@@ -98,6 +101,8 @@ public class RoomMenuPanel extends Panel {
 	private ClientManager cm;
 	@SpringBean
 	private ConfigurationDao cfgDao;
+	@SpringBean
+	private KurentoHandler kHandler;
 
 	public RoomMenuPanel(String id, final RoomPanel room) {
 		super(id);
@@ -204,20 +209,11 @@ public class RoomMenuPanel extends Panel {
 		menuPanel.update(handler);
 		StringBuilder roomClass = new StringBuilder("room name");
 		StringBuilder roomTitle = new StringBuilder();
-		if (room.getRecordingUser() != null) {
-			Client recClient = cm.getBySid(room.getRecordingUser());
-			if (recClient != null) {
+		if (kHandler.isRecording(r.getId())) {
+			JSONObject ru = kHandler.getRecordingUser(r.getId());
+			if (!Strings.isEmpty(ru.optString("login"))) {
 				roomTitle.append(String.format("%s %s %s %s %s", getString("419")
-						, recClient.getUser().getLogin(), recClient.getUser().getFirstname(), recClient.getUser().getLastname(), df.format(recClient.getConnectedSince())));
-				roomClass.append(" screen");
-			}
-			Client pubClient = cm.getBySid(room.getPublishingUser());
-			if (pubClient != null) {
-				if (recClient != null) {
-					roomTitle.append('\n');
-				}
-				roomTitle.append(String.format("%s %s %s %s %s", getString("1504")
-						, pubClient.getUser().getLogin(), pubClient.getUser().getFirstname(), pubClient.getUser().getLastname(), "URL"));
+						, ru.getString("login"), ru.getString("firstName"), ru.getString("lastName"), df.format(new Date(ru.getLong("started")))));
 				roomClass.append(" screen");
 			}
 		}
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/raw-video-manager.js b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/raw-video-manager.js
index 2ef74eb..00f9014 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/raw-video-manager.js
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/raw-video-manager.js
@@ -92,7 +92,7 @@ var VideoManager = (function() {
 			return;
 		}
 		c.streams.forEach(function(sd) {
-			if (sd.self && (VideoUtil.isSharing(sd) || VideoUtil.isRecording(sd))) {
+			if (sd.self && VideoUtil.isSharing(sd)) {
 				return;
 			}
 			const _id = VideoUtil.getVid(sd.uid)
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/raw-video-util.js b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/raw-video-util.js
index eebc2d3..e8c6d86 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/raw-video-util.js
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/raw-video-util.js
@@ -12,13 +12,6 @@ var VideoUtil = (function() {
 	function _isSharing(sd) {
 		return 'screen' === sd.type;
 	}
-	function _isRecording(c) {
-		return false;
-		/*FIXME TODO return 'sharing' === c.type
-			&& c.screenActivities.includes('recording')
-			&& !c.screenActivities.includes('sharing');
-		*/
-	}
 	function _hasAudio(sd) {
 		return !sd || sd.activities.includes(MIC_ACTIVITY);
 	}
@@ -133,7 +126,6 @@ var VideoUtil = (function() {
 
 	self.getVid = _getVid;
 	self.isSharing = _isSharing;
-	self.isRecording = _isRecording;
 	self.hasAudio = _hasAudio;
 	self.hasVideo = _hasVideo;
 	self.getRects = _getRects;
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/InterviewWbPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/InterviewWbPanel.java
index 1bd8e88..9a9b304 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/InterviewWbPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/InterviewWbPanel.java
@@ -20,18 +20,23 @@ package org.apache.openmeetings.web.room.wb;
 
 import java.io.IOException;
 
+import org.apache.openmeetings.core.remote.KurentoHandler;
+import org.apache.openmeetings.db.entity.basic.Client;
 import org.apache.openmeetings.db.entity.file.BaseFileItem;
 import org.apache.openmeetings.db.entity.room.Room.Right;
 import org.apache.openmeetings.web.room.RoomPanel;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.request.resource.JavaScriptResourceReference;
 import org.apache.wicket.request.resource.ResourceReference;
+import org.apache.wicket.spring.injection.annot.SpringBean;
 
 import com.github.openjson.JSONObject;
 
 public class InterviewWbPanel extends AbstractWbPanel {
 	private static final long serialVersionUID = 1L;
 	public static final ResourceReference INTERVIEWWB_JS_REFERENCE = new JavaScriptResourceReference(WbPanel.class, "interviewwb.js");
+	@SpringBean
+	private KurentoHandler kurento;
 
 	public InterviewWbPanel(String id, RoomPanel rp) {
 		super(id, rp);
@@ -49,24 +54,18 @@ public class InterviewWbPanel extends AbstractWbPanel {
 
 	@Override
 	protected void processWbAction(WbAction a, JSONObject obj, AjaxRequestTarget target) throws IOException {
-		/*
 		Client c = rp.getClient();
-		if (c.hasRight(Room.Right.moderator)) {
+		if (c.hasRight(Right.moderator)) {
 			switch (a) {
 				case startRecording:
-					if (getBean(StreamClientManager.class).getRecordingCount(c.getRoomId()) < 1) {
-						getBean(ScopeApplicationAdapter.class).startInterviewRecording(c);
-					}
+					kurento.startRecording(c);
 					break;
 				case stopRecording:
-					if (getBean(StreamClientManager.class).getRecordingCount(c.getRoomId()) < 1) {
-						getBean(ScopeApplicationAdapter.class).stopInterviewRecording(c);
-					}
+					kurento.stopRecording(c);
 					break;
 				default:
 					//no-op
 			}
 		}
-		*/
 	}
 }
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.java
index a438940..1ab34fd 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.java
@@ -18,6 +18,7 @@
  */
 package org.apache.openmeetings.web.room.wb;
 
+import static java.util.UUID.randomUUID;
 import static org.apache.openmeetings.db.dto.room.Whiteboard.ATTR_FILE_ID;
 import static org.apache.openmeetings.db.dto.room.Whiteboard.ATTR_FILE_TYPE;
 import static org.apache.openmeetings.db.dto.room.Whiteboard.ATTR_SLIDE;
@@ -46,7 +47,6 @@ import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
 import java.util.TreeMap;
-import java.util.UUID;
 import java.util.function.Consumer;
 import java.util.function.Function;
 
@@ -550,7 +550,7 @@ public class WbPanel extends AbstractWbPanel {
 	public void sendFileToWb(final BaseFileItem fi, boolean clean) {
 		if (isVisible() && fi.getId() != null) {
 			Whiteboards wbs = wbm.get(roomId);
-			String wuid = UUID.randomUUID().toString();
+			String wuid = randomUUID().toString();
 			Whiteboard wb = wbs.get(wbs.getActiveWb());
 			if (wb == null) {
 				return;
@@ -662,7 +662,7 @@ public class WbPanel extends AbstractWbPanel {
 		FileItem f = new FileItem();
 		f.setType(BaseFileItem.Type.WmlFile);
 		f.setRoomId(roomId);
-		f.setHash(UUID.randomUUID().toString());
+		f.setHash(randomUUID().toString());
 		f.setName(name);
 		f = fileDao.update(f);
 		return wb.save(f.getFile().toPath());
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/util/CallbackFunctionHelper.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/util/CallbackFunctionHelper.java
index dca35d9..c9d1808 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/util/CallbackFunctionHelper.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/util/CallbackFunctionHelper.java
@@ -16,8 +16,9 @@
  */
 package org.apache.openmeetings.web.util;
 
+import static java.util.UUID.randomUUID;
+
 import java.io.Serializable;
-import java.util.UUID;
 
 import org.apache.wicket.AttributeModifier;
 import org.apache.wicket.Component;
@@ -54,7 +55,7 @@ public class CallbackFunctionHelper {
 	}
 
 	public static JavaScriptHeaderItem getNamedFunction(String name, AbstractDefaultAjaxBehavior b, CallbackParameter... extraParameters) {
-		String uid = UUID.randomUUID().toString();
+		String uid = randomUUID().toString();
 		return JavaScriptHeaderItem.forScript(getNamedFunctionStr(name, b, extraParameters), String.format("%s-%s", name, uid));
 	}
 
diff --git a/openmeetings-web/src/test/java/org/apache/openmeetings/AbstractJUnitDefaults.java b/openmeetings-web/src/test/java/org/apache/openmeetings/AbstractJUnitDefaults.java
index fc47a04..6b4809e 100644
--- a/openmeetings-web/src/test/java/org/apache/openmeetings/AbstractJUnitDefaults.java
+++ b/openmeetings-web/src/test/java/org/apache/openmeetings/AbstractJUnitDefaults.java
@@ -18,6 +18,7 @@
  */
 package org.apache.openmeetings;
 
+import static java.util.UUID.randomUUID;
 import static org.apache.openmeetings.db.util.ApplicationHelper.ensureApplication;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.DEFAULT_CONTEXT_NAME;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.getCryptClassName;
@@ -27,7 +28,6 @@ import static org.junit.Assert.assertNotNull;
 
 import java.util.Date;
 import java.util.Random;
-import java.util.UUID;
 
 import org.apache.openmeetings.db.dao.basic.ConfigurationDao;
 import org.apache.openmeetings.db.dao.calendar.AppointmentDao;
@@ -157,7 +157,7 @@ public abstract class AbstractJUnitDefaults extends AbstractSpringTest {
 	}
 
 	public User getUser() throws Exception {
-		return getUser(UUID.randomUUID().toString());
+		return getUser(randomUUID().toString());
 	}
 
 	protected static String getLogin(String uid) {
@@ -188,7 +188,7 @@ public abstract class AbstractJUnitDefaults extends AbstractSpringTest {
 	}
 
 	public User createUser() throws Exception {
-		return createUser(UUID.randomUUID().toString());
+		return createUser(randomUUID().toString());
 	}
 
 	public User createUser(String uuid) throws Exception {
@@ -214,17 +214,17 @@ public abstract class AbstractJUnitDefaults extends AbstractSpringTest {
 		cfg.setTimeZone(timeZone);
 		importInitvalues.loadAll(cfg, false);
 		// regular user
-		importInitvalues.createSystemUser(getUser(UUID.randomUUID().toString()), group, regularUsername, userpass, false, null);
+		importInitvalues.createSystemUser(getUser(randomUUID().toString()), group, regularUsername, userpass, false, null);
 
 		// soap user
-		importInitvalues.createSystemUser(getUser(UUID.randomUUID().toString()), group, soapUsername, userpass, false, u -> {
+		importInitvalues.createSystemUser(getUser(randomUUID().toString()), group, soapUsername, userpass, false, u -> {
 			u.getRights().remove(User.Right.Room);
 			u.getRights().remove(User.Right.Dashboard);
 			u.getRights().add(User.Right.Soap);
 		});
 
 		// group admin
-		importInitvalues.createSystemUser(getUser(UUID.randomUUID().toString()), group, groupAdminUsername, userpass, true, null);
+		importInitvalues.createSystemUser(getUser(randomUUID().toString()), group, groupAdminUsername, userpass, true, null);
 	}
 
 	public User getContact(String uuid, Long ownerId) {
@@ -232,7 +232,7 @@ public abstract class AbstractJUnitDefaults extends AbstractSpringTest {
 	}
 
 	public User createUserContact(Long ownerId) {
-		return createUserContact(getContact(UUID.randomUUID().toString(), ownerId), ownerId);
+		return createUserContact(getContact(randomUUID().toString(), ownerId), ownerId);
 	}
 
 	public User createUserContact(User user, Long ownerId) {
diff --git a/openmeetings-web/src/test/java/org/apache/openmeetings/calendar/TestAppointmentAddAppointment.java b/openmeetings-web/src/test/java/org/apache/openmeetings/calendar/TestAppointmentAddAppointment.java
index 733f06a..4df4f0f 100644
--- a/openmeetings-web/src/test/java/org/apache/openmeetings/calendar/TestAppointmentAddAppointment.java
+++ b/openmeetings-web/src/test/java/org/apache/openmeetings/calendar/TestAppointmentAddAppointment.java
@@ -18,6 +18,7 @@
  */
 package org.apache.openmeetings.calendar;
 
+import static java.util.UUID.randomUUID;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
@@ -27,7 +28,6 @@ import java.time.ZoneId;
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Date;
-import java.util.UUID;
 
 import org.apache.openmeetings.AbstractWicketTester;
 import org.apache.openmeetings.db.dao.calendar.AppointmentDao;
@@ -162,7 +162,7 @@ public class TestAppointmentAddAppointment extends AbstractWicketTester {
 		a.getRoom().setType(Room.Type.conference);
 		for (int i = 0; i < 3; ++i) {
 			MeetingMember mm = new MeetingMember();
-			mm.setUser(getContact(UUID.randomUUID().toString(), owner.getId()));
+			mm.setUser(getContact(randomUUID().toString(), owner.getId()));
 			a.getMeetingMembers().add(mm);
 		}
 		a = appointmentDao.update(a, owner.getId());
diff --git a/openmeetings-web/src/test/java/org/apache/openmeetings/calendar/TestSendIcalMessage.java b/openmeetings-web/src/test/java/org/apache/openmeetings/calendar/TestSendIcalMessage.java
index 3318070..cc027a6 100644
--- a/openmeetings-web/src/test/java/org/apache/openmeetings/calendar/TestSendIcalMessage.java
+++ b/openmeetings-web/src/test/java/org/apache/openmeetings/calendar/TestSendIcalMessage.java
@@ -18,6 +18,8 @@
  */
 package org.apache.openmeetings.calendar;
 
+import static java.util.UUID.randomUUID;
+
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.net.URI;
@@ -27,7 +29,6 @@ import java.util.Date;
 import java.util.GregorianCalendar;
 import java.util.List;
 import java.util.Map;
-import java.util.UUID;
 
 import javax.activation.DataHandler;
 import javax.mail.BodyPart;
@@ -114,7 +115,7 @@ public class TestSendIcalMessage extends AbstractJUnitDefaults {
 		meeting.getProperties().add(tz.getTimeZoneId());
 
 		// generate unique identifier..
-		Uid uid = new Uid(UUID.randomUUID().toString());
+		Uid uid = new Uid(randomUUID().toString());
 		meeting.getProperties().add(uid);
 
 		// add attendees..
diff --git a/openmeetings-web/src/test/java/org/apache/openmeetings/core/file/TestFileProcessor.java b/openmeetings-web/src/test/java/org/apache/openmeetings/core/file/TestFileProcessor.java
index 2b77103..69af09d 100644
--- a/openmeetings-web/src/test/java/org/apache/openmeetings/core/file/TestFileProcessor.java
+++ b/openmeetings-web/src/test/java/org/apache/openmeetings/core/file/TestFileProcessor.java
@@ -18,6 +18,7 @@
  */
 package org.apache.openmeetings.core.file;
 
+import static java.util.UUID.randomUUID;
 import static org.apache.openmeetings.util.OmFileHelper.FILE_NAME_FMT;
 import static org.apache.openmeetings.util.OmFileHelper.getDefaultProfilePicture;
 import static org.junit.Assert.assertEquals;
@@ -25,7 +26,6 @@ import static org.junit.Assert.assertFalse;
 
 import java.io.FileInputStream;
 import java.io.InputStream;
-import java.util.UUID;
 
 import org.apache.openmeetings.AbstractJUnitDefaults;
 import org.apache.openmeetings.core.data.file.FileProcessor;
@@ -47,7 +47,7 @@ public class TestFileProcessor extends AbstractJUnitDefaults {
 		for (String ext : new String[] {null, "txt", "jpg"}) {
 			FileItem f = new FileItemDTO()
 					.setName(String.format(FILE_NAME_FMT, FILE_NAME, ext))
-					.setHash(UUID.randomUUID().toString())
+					.setHash(randomUUID().toString())
 					.setType(BaseFileItem.Type.Recording).get();
 			try (InputStream is = new FileInputStream(getDefaultProfilePicture())) {
 				ProcessResultList result = processor.processFile(f, is);
diff --git a/openmeetings-web/src/test/java/org/apache/openmeetings/user/TestUserContact.java b/openmeetings-web/src/test/java/org/apache/openmeetings/user/TestUserContact.java
index df0165a..3c4f4d2 100644
--- a/openmeetings-web/src/test/java/org/apache/openmeetings/user/TestUserContact.java
+++ b/openmeetings-web/src/test/java/org/apache/openmeetings/user/TestUserContact.java
@@ -18,6 +18,7 @@
  */
 package org.apache.openmeetings.user;
 
+import static java.util.UUID.randomUUID;
 import static org.apache.openmeetings.web.app.WebSession.getUserId;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -25,7 +26,6 @@ import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import java.util.List;
-import java.util.UUID;
 
 import org.apache.openmeetings.AbstractWicketTester;
 import org.apache.openmeetings.db.entity.user.GroupUser;
@@ -41,7 +41,7 @@ public class TestUserContact extends AbstractWicketTester {
 
 	@Test
 	public void createUserWithGroup() throws Exception {
-		String uuid = UUID.randomUUID().toString();
+		String uuid = randomUUID().toString();
 		User u = getUser(uuid);
 		u.getGroupUsers().add(new GroupUser(groupDao.get(1L), u));
 		u = userDao.update(u, null);
@@ -55,7 +55,7 @@ public class TestUserContact extends AbstractWicketTester {
 
 	@Test
 	public void testCreateUser() throws Exception {
-		String uuid = UUID.randomUUID().toString();
+		String uuid = randomUUID().toString();
 		User u = createUser(uuid);
 		assertTrue("Password should be set as expected", userDao.verifyPassword(u.getId(), createPass()));
 	}
diff --git a/openmeetings-web/src/test/java/org/apache/openmeetings/user/TestUserGroup.java b/openmeetings-web/src/test/java/org/apache/openmeetings/user/TestUserGroup.java
index c46e754..e5dacf2 100644
--- a/openmeetings-web/src/test/java/org/apache/openmeetings/user/TestUserGroup.java
+++ b/openmeetings-web/src/test/java/org/apache/openmeetings/user/TestUserGroup.java
@@ -18,12 +18,12 @@
  */
 package org.apache.openmeetings.user;
 
+import static java.util.UUID.randomUUID;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import java.util.List;
-import java.util.UUID;
 
 import org.apache.openmeetings.AbstractJUnitDefaults;
 import org.apache.openmeetings.db.dao.user.GroupUserDao;
@@ -75,7 +75,7 @@ public class TestUserGroup extends AbstractJUnitDefaults {
 
 	@Test
 	public void addUserWithoutGroup() throws Exception {
-		String uuid = UUID.randomUUID().toString();
+		String uuid = randomUUID().toString();
 		User u = getUser(uuid);
 		u = userDao.update(u, null);
 		assertNotNull("User successfully created", u.getId());
diff --git a/openmeetings-web/src/test/java/org/apache/openmeetings/web/TestCalendar.java b/openmeetings-web/src/test/java/org/apache/openmeetings/web/TestCalendar.java
index 5d4b9dc..5523b89 100644
--- a/openmeetings-web/src/test/java/org/apache/openmeetings/web/TestCalendar.java
+++ b/openmeetings-web/src/test/java/org/apache/openmeetings/web/TestCalendar.java
@@ -18,11 +18,11 @@
  */
 package org.apache.openmeetings.web;
 
+import static java.util.UUID.randomUUID;
 import static org.junit.Assert.assertEquals;
 
 import java.time.LocalDateTime;
 import java.util.List;
-import java.util.UUID;
 
 import org.apache.openmeetings.AbstractWicketTester;
 import org.apache.openmeetings.db.dao.calendar.AppointmentDao;
@@ -67,7 +67,7 @@ public class TestCalendar extends AbstractWicketTester {
 			tester.executeBehavior((AbstractAjaxBehavior)cal.get("form:calendar").getBehaviorById(0)); //select-event
 			FormTester appTester = tester.newFormTester(PATH_APPOINTMENT_DLG_FRM);
 			//check inviteeType:groupContainer:groups is invisible for regular user
-			String title = String.format("title%s", UUID.randomUUID());
+			String title = String.format("title%s", randomUUID());
 			appTester.setValue("title", title);
 			ButtonAjaxBehavior save = getButtonBehavior(PATH_APPOINTMENT_DLG, "save");
 			tester.executeBehavior(save);
diff --git a/openmeetings-web/src/test/java/org/apache/openmeetings/web/pages/auth/TestLoginUI.java b/openmeetings-web/src/test/java/org/apache/openmeetings/web/pages/auth/TestLoginUI.java
index 1501f8a..472bdd6 100644
--- a/openmeetings-web/src/test/java/org/apache/openmeetings/web/pages/auth/TestLoginUI.java
+++ b/openmeetings-web/src/test/java/org/apache/openmeetings/web/pages/auth/TestLoginUI.java
@@ -18,6 +18,7 @@
  */
 package org.apache.openmeetings.web.pages.auth;
 
+import static java.util.UUID.randomUUID;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_EMAIL_AT_REGISTER;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_EMAIL_VERIFICATION;
 import static org.junit.Assert.assertFalse;
@@ -28,7 +29,6 @@ import static org.junit.Assert.assertTrue;
 import java.lang.reflect.Method;
 import java.util.List;
 import java.util.Locale;
-import java.util.UUID;
 
 import org.apache.openmeetings.AbstractWicketTester;
 import org.apache.openmeetings.db.entity.basic.Configuration;
@@ -101,7 +101,7 @@ public class TestLoginUI extends AbstractWicketTester {
 		tester.startPage(SignInPage.class);
 		tester.assertRenderedPage(SignInPage.class);
 
-		String uid = UUID.randomUUID().toString();
+		String uid = randomUUID().toString();
 		performRegister(uid, "account.created");
 	}
 
@@ -124,12 +124,12 @@ public class TestLoginUI extends AbstractWicketTester {
 		tester.startPage(SignInPage.class);
 		tester.assertRenderedPage(SignInPage.class);
 
-		performForget(UUID.randomUUID().toString());
+		performForget(randomUUID().toString());
 	}
 
 	@Test
 	public void testReset() {
-		tester.startPage(ResetPage.class, new PageParameters().add(ResetPage.RESET_PARAM, UUID.randomUUID().toString()));
+		tester.startPage(ResetPage.class, new PageParameters().add(ResetPage.RESET_PARAM, randomUUID().toString()));
 		tester.assertRenderedPage(SignInPage.class);
 	}
 
diff --git a/openmeetings-web/src/test/java/org/apache/openmeetings/webservice/AbstractWebServiceTest.java b/openmeetings-web/src/test/java/org/apache/openmeetings/webservice/AbstractWebServiceTest.java
index eb7a2c8..d2ac7fd 100644
--- a/openmeetings-web/src/test/java/org/apache/openmeetings/webservice/AbstractWebServiceTest.java
+++ b/openmeetings-web/src/test/java/org/apache/openmeetings/webservice/AbstractWebServiceTest.java
@@ -18,6 +18,7 @@
  */
 package org.apache.openmeetings.webservice;
 
+import static java.util.UUID.randomUUID;
 import static javax.ws.rs.core.MediaType.APPLICATION_FORM_URLENCODED;
 import static org.apache.openmeetings.AbstractJUnitDefaults.createPass;
 import static org.apache.openmeetings.AbstractJUnitDefaults.ensureSchema;
@@ -37,7 +38,6 @@ import java.nio.file.Files;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
-import java.util.UUID;
 
 import javax.ws.rs.core.Form;
 import javax.ws.rs.core.MediaType;
@@ -113,7 +113,7 @@ public class AbstractWebServiceTest {
 		connector.setPort(0);
 		tomcat.getService().addConnector(connector);
 		tomcat.setConnector(connector);
-		File wd = Files.createTempDirectory("om" + UUID.randomUUID().toString()).toFile();
+		File wd = Files.createTempDirectory("om" + randomUUID().toString()).toFile();
 		tomcat.setBaseDir(wd.getCanonicalPath());
 		tomcat.getHost().setAppBase(wd.getCanonicalPath());
 		tomcat.getHost().setAutoDeploy(false);
@@ -187,7 +187,7 @@ public class AbstractWebServiceTest {
 		try (InputStream is = new FileInputStream(fsFile)) {
 			FileItemDTO file = new FileItemDTO()
 					.setName(name)
-					.setHash(UUID.randomUUID().toString())
+					.setHash(randomUUID().toString())
 					.setType(type);
 			List<Attachment> atts = new ArrayList<>();
 			atts.add(new Attachment("file", MediaType.APPLICATION_JSON, file));
diff --git a/openmeetings-web/src/test/java/org/apache/openmeetings/webservice/TestCalendarService.java b/openmeetings-web/src/test/java/org/apache/openmeetings/webservice/TestCalendarService.java
index 2526911..b7e54b0 100644
--- a/openmeetings-web/src/test/java/org/apache/openmeetings/webservice/TestCalendarService.java
+++ b/openmeetings-web/src/test/java/org/apache/openmeetings/webservice/TestCalendarService.java
@@ -18,6 +18,7 @@
  */
 package org.apache.openmeetings.webservice;
 
+import static java.util.UUID.randomUUID;
 import static org.apache.openmeetings.AbstractJUnitDefaults.ONE_HOUR;
 import static org.apache.openmeetings.AbstractJUnitDefaults.createPass;
 import static org.apache.openmeetings.AbstractJUnitDefaults.createUser;
@@ -32,7 +33,6 @@ import static org.junit.Assert.assertNull;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
-import java.util.UUID;
 
 import javax.ws.rs.core.Form;
 import javax.ws.rs.core.Response;
@@ -62,7 +62,7 @@ public class TestCalendarService extends AbstractWebServiceTest {
 	public static final String CALENDAR_SERVICE_MOUNT = "calendar";
 
 	private void actualTest(Room r) throws Exception {
-		String uuid = UUID.randomUUID().toString();
+		String uuid = randomUUID().toString();
 		User u = getUser(uuid);
 		u.getGroupUsers().add(new GroupUser(getBean(GroupDao.class).get(1L), u));
 		webCreateUser(u);
@@ -125,7 +125,7 @@ public class TestCalendarService extends AbstractWebServiceTest {
 	}
 
 	private String loginNewUser() throws Exception {
-		String uuid = UUID.randomUUID().toString();
+		String uuid = randomUUID().toString();
 		User u = getUser(uuid);
 		u.getGroupUsers().add(new GroupUser(getBean(GroupDao.class).get(1L), u));
 		webCreateUser(u);
@@ -175,7 +175,7 @@ public class TestCalendarService extends AbstractWebServiceTest {
 						.put(new JSONObject().put("user", new JSONObject()
 								.put("id", 1))));
 
-		String uuid = UUID.randomUUID().toString();
+		String uuid = randomUUID().toString();
 		User u = getUser(uuid);
 		u.getGroupUsers().add(new GroupUser(getBean(GroupDao.class).get(1L), u));
 		u = createUser(getBean(UserDao.class), u);
@@ -284,7 +284,7 @@ public class TestCalendarService extends AbstractWebServiceTest {
 
 	@Test
 	public void testGetByTitle() throws Exception {
-		String title = "title" + UUID.randomUUID().toString();
+		String title = "title" + randomUUID().toString();
 		String sid = createApp(title);
 		@SuppressWarnings("unchecked")
 		List<AppointmentDTO> list = (List<AppointmentDTO>)getClient(getCalendarUrl())
@@ -295,7 +295,7 @@ public class TestCalendarService extends AbstractWebServiceTest {
 		assertEquals("List of one item should be returned", 1, list.size());
 		assertEquals("Title should match", title, list.get(0).getTitle());
 
-		title = UUID.randomUUID().toString();
+		title = randomUUID().toString();
 		@SuppressWarnings("unchecked")
 		List<AppointmentDTO> list1 = (List<AppointmentDTO>)getClient(getCalendarUrl())
 			.path(String.format("/title/%s", title))
diff --git a/openmeetings-web/src/test/java/org/apache/openmeetings/webservice/TestErrorService.java b/openmeetings-web/src/test/java/org/apache/openmeetings/webservice/TestErrorService.java
index 281788f..b1bcaf1 100644
--- a/openmeetings-web/src/test/java/org/apache/openmeetings/webservice/TestErrorService.java
+++ b/openmeetings-web/src/test/java/org/apache/openmeetings/webservice/TestErrorService.java
@@ -18,12 +18,12 @@
  */
 package org.apache.openmeetings.webservice;
 
+import static java.util.UUID.randomUUID;
 import static org.apache.openmeetings.AbstractJUnitDefaults.rnd;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
 import java.util.Locale;
-import java.util.UUID;
 
 import javax.ws.rs.core.Response;
 
@@ -39,7 +39,7 @@ public class TestErrorService extends AbstractWebServiceTest {
 	public void getTestBadKey() {
 		Locale[] locales = Locale.getAvailableLocales();
 		ServiceResult sr = getClient(getErrorUrl())
-				.path(String.format("/%s/%s", UUID.randomUUID().toString(), LabelDao.getLanguage(locales[rnd.nextInt(locales.length)], 1L)))
+				.path(String.format("/%s/%s", randomUUID().toString(), LabelDao.getLanguage(locales[rnd.nextInt(locales.length)], 1L)))
 				.get(ServiceResult.class);
 		assertNotNull("Valid Result should be returned", sr);
 		assertEquals("SUCCESS result should be returned", ServiceResult.Type.SUCCESS.name(), sr.getType());
diff --git a/openmeetings-web/src/test/java/org/apache/openmeetings/webservice/TestRecordingService.java b/openmeetings-web/src/test/java/org/apache/openmeetings/webservice/TestRecordingService.java
index 05ac4e7..f235442 100644
--- a/openmeetings-web/src/test/java/org/apache/openmeetings/webservice/TestRecordingService.java
+++ b/openmeetings-web/src/test/java/org/apache/openmeetings/webservice/TestRecordingService.java
@@ -18,13 +18,13 @@
  */
 package org.apache.openmeetings.webservice;
 
+import static java.util.UUID.randomUUID;
 import static org.apache.openmeetings.AbstractJUnitDefaults.getUser;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
 import java.util.Collection;
-import java.util.UUID;
 
 import org.apache.openmeetings.db.dao.record.RecordingDao;
 import org.apache.openmeetings.db.dto.basic.ServiceResult;
@@ -37,7 +37,7 @@ public class TestRecordingService extends AbstractWebServiceTest {
 	public static final String RECORD_SERVICE_MOUNT = "record";
 
 	private User getExternalUser() throws Exception {
-		String uuid = UUID.randomUUID().toString();
+		String uuid = randomUUID().toString();
 		User u = getUser(uuid);
 		u.setExternalType(UNIT_TEST_EXT_TYPE);
 		u.setExternalId(uuid);
diff --git a/openmeetings-web/src/test/java/org/apache/openmeetings/webservice/TestRoomService.java b/openmeetings-web/src/test/java/org/apache/openmeetings/webservice/TestRoomService.java
index ef00c99..601c4b9 100644
--- a/openmeetings-web/src/test/java/org/apache/openmeetings/webservice/TestRoomService.java
+++ b/openmeetings-web/src/test/java/org/apache/openmeetings/webservice/TestRoomService.java
@@ -18,6 +18,7 @@
  */
 package org.apache.openmeetings.webservice;
 
+import static java.util.UUID.randomUUID;
 import static org.apache.openmeetings.util.OmFileHelper.getDefaultProfilePicture;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -26,7 +27,6 @@ import static org.junit.Assert.assertTrue;
 
 import java.io.IOException;
 import java.util.List;
-import java.util.UUID;
 
 import org.apache.openmeetings.db.dao.room.RoomDao;
 import org.apache.openmeetings.db.dto.basic.ServiceResult;
@@ -46,7 +46,7 @@ public class TestRoomService extends AbstractWebServiceTest {
 	@Test
 	public void testExternal() {
 		ServiceResult sr = login();
-		String extId = UUID.randomUUID().toString();
+		String extId = randomUUID().toString();
 		Room.Type type = Room.Type.presentation;
 		String name = "Unit Test Ext Room";
 		String comment = "Unit Test Ext Room Comments";
@@ -71,7 +71,7 @@ public class TestRoomService extends AbstractWebServiceTest {
 
 	@Test
 	public void testCreate1() {
-		String extId = UUID.randomUUID().toString();
+		String extId = randomUUID().toString();
 		Room.Type type = Room.Type.presentation;
 		String name = "Unit Test Ext Room1";
 		String comment = "Unit Test Ext Room Comments1";
diff --git a/openmeetings-web/src/test/java/org/apache/openmeetings/webservice/TestUserService.java b/openmeetings-web/src/test/java/org/apache/openmeetings/webservice/TestUserService.java
index 2038f65..aacce56 100644
--- a/openmeetings-web/src/test/java/org/apache/openmeetings/webservice/TestUserService.java
+++ b/openmeetings-web/src/test/java/org/apache/openmeetings/webservice/TestUserService.java
@@ -18,6 +18,7 @@
  */
 package org.apache.openmeetings.webservice;
 
+import static java.util.UUID.randomUUID;
 import static javax.ws.rs.core.MediaType.APPLICATION_FORM_URLENCODED;
 import static org.apache.openmeetings.AbstractJUnitDefaults.adminUsername;
 import static org.apache.openmeetings.AbstractJUnitDefaults.createPass;
@@ -33,7 +34,6 @@ import static org.junit.Assert.assertTrue;
 import java.util.Collection;
 import java.util.Locale;
 import java.util.TimeZone;
-import java.util.UUID;
 
 import javax.ws.rs.core.Form;
 import javax.ws.rs.core.Response;
@@ -125,7 +125,7 @@ public class TestUserService extends AbstractWebServiceTest {
 		String tz = TimeZone.getTimeZone(tzList[rnd.nextInt(tzList.length)]).getID();
 		ServiceResult r = login();
 		UserDTO u = new UserDTO();
-		String uuid = UUID.randomUUID().toString();
+		String uuid = randomUUID().toString();
 		u.setLogin("test" + uuid);
 		u.setPassword(createPass());
 		u.setFirstname("testF" + uuid);