Author: sebawagner Date: Fri Dec 14 07:41:54 2012 New Revision: 1421702 URL: http://svn.apache.org/viewvc?rev=1421702&view=rev Log: OPENMEETINGS-460 Fixes: When a user is hosted on a slave and uploads a document, the document is uploaded via HTTP and send to the server. The upload complete message has to be send to the slave server first, the master can't send a upload complete message. Also fixes a Bug + provides a JUnit test for an bug in the getServerForSession method
Added: incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/test/cluster/TestHashMapStoreSyncBug.java Modified: incubator/openmeetings/trunk/singlewebapp/WebContent/src/base/components/presenter/guiPresenter.lzx incubator/openmeetings/trunk/singlewebapp/WebContent/src/base/components/upload/uploadWindowExplorer.lzx incubator/openmeetings/trunk/singlewebapp/WebContent/src/modules/admin/connections/roomClient.lzx incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/axis/services/RoomWebService.java incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/axis/services/RoomWebServiceFacade.java incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/cluster/beans/ServerDTO.java incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/cluster/sync/RestClient.java incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/documents/beans/UploadCompleteMessage.java incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/quartz/scheduler/ClusterSlaveJob.java incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/servlet/outputhandler/BackupImportController.java incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/servlet/outputhandler/ImportController.java incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/servlet/outputhandler/UploadController.java Modified: incubator/openmeetings/trunk/singlewebapp/WebContent/src/base/components/presenter/guiPresenter.lzx URL: http://svn.apache.org/viewvc/incubator/openmeetings/trunk/singlewebapp/WebContent/src/base/components/presenter/guiPresenter.lzx?rev=1421702&r1=1421701&r2=1421702&view=diff ============================================================================== --- incubator/openmeetings/trunk/singlewebapp/WebContent/src/base/components/presenter/guiPresenter.lzx (original) +++ incubator/openmeetings/trunk/singlewebapp/WebContent/src/base/components/presenter/guiPresenter.lzx Fri Dec 14 07:41:54 2012 @@ -128,6 +128,10 @@ <handler name="onwidth" args="w"> <![CDATA[ if (this.isresizeing){ + if (w<40){ + this.setAttribute('width',41); + this._resizeview.onmouseup.sendEvent(); + } if (w<_titlebar._title.width+70) { this.setAttribute('width',_titlebar._title.width+71); this._resizeview.onmouseup.sendEvent(); @@ -454,7 +458,7 @@ colorTo="$once{ canvas.getThemeColor('styleMenuBarBaseColor') }" > </gradientview> - <text fontsize="10" height="17" x="6" y="1" text="${ this.parent.parent.title }" + <text name="_title" fontsize="10" height="17" x="6" y="1" text="${ this.parent.parent.title }" fgcolor="0xFFFFFF" resize="true" fontstyle="bold" /> <view name="_toolbar" visibility="$once{ ((parent.parent.fullToolBar) ? 'visible' : 'hidden' ) }" Modified: incubator/openmeetings/trunk/singlewebapp/WebContent/src/base/components/upload/uploadWindowExplorer.lzx URL: http://svn.apache.org/viewvc/incubator/openmeetings/trunk/singlewebapp/WebContent/src/base/components/upload/uploadWindowExplorer.lzx?rev=1421702&r1=1421701&r2=1421702&view=diff ============================================================================== --- incubator/openmeetings/trunk/singlewebapp/WebContent/src/base/components/upload/uploadWindowExplorer.lzx (original) +++ incubator/openmeetings/trunk/singlewebapp/WebContent/src/base/components/upload/uploadWindowExplorer.lzx Fri Dec 14 07:41:54 2012 @@ -116,23 +116,21 @@ if (parent._loadToWhiteboard.getValue()) { - var fileExplorerItem = tArrayValueObj.fileExplorerItem; - if ($debug) Debug.write(tArrayValueObj); - if (fileExplorerItem.isPresentation) { - var url = this.formatURL(fileExplorerItem.fileHash); + if (tArrayValueObj.isPresentation) { + var url = this.formatURL(tArrayValueObj.fileHash); - var uploadmoduleimgfolderVar = '/' + fileExplorerItem.fileHash; + var uploadmoduleimgfolderVar = '/' + tArrayValueObj.fileHash; if ($debug) Debug.write(url); - canvas._drawarea.loadSWFPresentationSynced(url,fileExplorerItem.fileHash + ".swf", + canvas._drawarea.loadSWFPresentationSynced(url,tArrayValueObj.fileHash + ".swf", "videoconf1",uploadmoduleimgfolderVar,"files",hib.conferencedomain,1, - fileExplorerItem.fileName); - } else if (fileExplorerItem.isImage) { - canvas._drawarea.parent.parent.clearAreaAndAddImage(this.generateFileLink(fileExplorerItem.fileHash),0,0, + tArrayValueObj.fileSystemName); + } else if (tArrayValueObj.isImage) { + canvas._drawarea.parent.parent.clearAreaAndAddImage(this.generateFileLink(tArrayValueObj.fileHash),0,0, canvas.getUrl() +'DownloadHandler', - fileExplorerItem.fileHash,"videoconf1","/","files",hib.conferencedomain); + tArrayValueObj.fileHash,"videoconf1","/","files",hib.conferencedomain); } } Modified: incubator/openmeetings/trunk/singlewebapp/WebContent/src/modules/admin/connections/roomClient.lzx URL: http://svn.apache.org/viewvc/incubator/openmeetings/trunk/singlewebapp/WebContent/src/modules/admin/connections/roomClient.lzx?rev=1421702&r1=1421701&r2=1421702&view=diff ============================================================================== --- incubator/openmeetings/trunk/singlewebapp/WebContent/src/modules/admin/connections/roomClient.lzx (original) +++ incubator/openmeetings/trunk/singlewebapp/WebContent/src/modules/admin/connections/roomClient.lzx Fri Dec 14 07:41:54 2012 @@ -78,8 +78,8 @@ var tServer = "master"; var serverId = 0; if (records[i].server != null) { - serverId = records[i].server.id; - tServer = "slave "+records[i].server.address+" ["+records[i].server.id+"]"; + serverId = records[i].server; + tServer = "slave " + " ["+serverId+"]"; } new lz.roomClientListItem(this._innerlist._inn._inn,{ Modified: incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/axis/services/RoomWebService.java URL: http://svn.apache.org/viewvc/incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/axis/services/RoomWebService.java?rev=1421702&r1=1421701&r2=1421702&view=diff ============================================================================== --- incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/axis/services/RoomWebService.java (original) +++ incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/axis/services/RoomWebService.java Fri Dec 14 07:41:54 2012 @@ -41,6 +41,7 @@ import org.apache.openmeetings.data.conf import org.apache.openmeetings.data.conference.Roommanagement; import org.apache.openmeetings.data.flvrecord.FlvRecordingDao; import org.apache.openmeetings.data.user.Usermanagement; +import org.apache.openmeetings.documents.beans.UploadCompleteMessage; import org.apache.openmeetings.persistence.beans.calendar.Appointment; import org.apache.openmeetings.persistence.beans.flvrecord.FlvRecording; import org.apache.openmeetings.persistence.beans.invitation.Invitations; @@ -2487,17 +2488,15 @@ public class RoomWebService { * @param paramName * @param paramValue * @return 1 in case of success, -2 if permissions are insufficient - * @throws AxisFault if any error ocurred + * @throws AxisFault if any error occurred */ public int modifyRoomParameter(String SID, Long room_id, String paramName, String paramValue) throws AxisFault { try { Long users_id = sessionManagement.checkSession(SID); Long user_level = userManagement.getUserLevelByID(users_id); - - log.debug("closeRoom 1 " + room_id); - if (authLevelManagement.checkWebServiceLevel(user_level)) { + log.debug("closeRoom 1 " + room_id); Rooms r = roomDao.get(room_id); PropertyUtils.setSimpleProperty(r, paramName, paramValue); roomDao.update(r, users_id); @@ -2511,4 +2510,51 @@ public class RoomWebService { throw new AxisFault(err.getMessage()); } } + + /** + * This method is used in cluster mode to send the sync event from the master to the slave + * + * @param SID The SID of the User. This SID must be marked as logged'in + * @param publicSID The publicSID that will receive the message + * @param userId part of sync message of document upload + * @param message part of sync message of document upload + * @param action part of sync message of document upload + * @param error part of sync message of document upload + * @param hasError part of sync message of document upload + * @param fileName part of sync message of document upload + * @param fileSystemName part of sync message of document upload + * @param isPresentation part of sync message of document upload + * @param isImage part of sync message of document upload + * @param isVideo part of sync message of document upload + * @param fileHash part of sync message of document upload + * @return + * @throws AxisFault if any error occurred + */ + public boolean syncUploadCompleteMessage(String SID, String publicSID, + Long userId, String message, String action, String error, + boolean hasError, String fileName, String fileSystemName, + boolean isPresentation, boolean isImage, boolean isVideo, + String fileHash) throws AxisFault { + + try { + Long users_id = sessionManagement.checkSession(SID); + Long user_level = userManagement.getUserLevelByID(users_id); + if (authLevelManagement.checkWebServiceLevel(user_level)) { + + scopeApplicationAdapter.sendMessageWithClientByPublicSID( + new UploadCompleteMessage(userId, message, action, + error, hasError, fileName, fileSystemName, + isPresentation, isImage, isVideo, fileHash), + publicSID); + + return true; + } + } catch (Exception err) { + log.error("[syncUploadCompleteMessage] ", err); + + throw new AxisFault(err.getMessage()); + } + return false; + } + } Modified: incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/axis/services/RoomWebServiceFacade.java URL: http://svn.apache.org/viewvc/incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/axis/services/RoomWebServiceFacade.java?rev=1421702&r1=1421701&r2=1421702&view=diff ============================================================================== --- incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/axis/services/RoomWebServiceFacade.java (original) +++ incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/axis/services/RoomWebServiceFacade.java Fri Dec 14 07:41:54 2012 @@ -51,7 +51,8 @@ public class RoomWebServiceFacade { private RoomWebService getRoomServiceProxy() { try { if (!ScopeApplicationAdapter.initComplete) { - throw new Exception("Server not yet initialized, retry in couple of seconds"); + throw new Exception( + "Server not yet initialized, retry in couple of seconds"); } ApplicationContext context = WebApplicationContextUtils .getWebApplicationContext(getServletContext()); @@ -669,8 +670,20 @@ public class RoomWebServiceFacade { return this.getRoomServiceProxy().closeRoom(SID, room_id, status); } - public int modifyRoomParameter(String SID, Long room_id, String paramName, String paramValue) - throws AxisFault { - return getRoomServiceProxy().modifyRoomParameter(SID, room_id, paramName, paramValue); + public int modifyRoomParameter(String SID, Long room_id, String paramName, + String paramValue) throws AxisFault { + return getRoomServiceProxy().modifyRoomParameter(SID, room_id, + paramName, paramValue); + } + + public boolean syncUploadCompleteMessage(String SID, String publicSID, + Long userId, String message, String action, String error, + boolean hasError, String fileName, String fileSystemName, + boolean isPresentation, boolean isImage, boolean isVideo, + String fileHash) throws AxisFault { + return getRoomServiceProxy().syncUploadCompleteMessage(SID, publicSID, + userId, message, action, error, hasError, fileName, + fileSystemName, isPresentation, isImage, isVideo, fileHash); } + } Modified: incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/cluster/beans/ServerDTO.java URL: http://svn.apache.org/viewvc/incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/cluster/beans/ServerDTO.java?rev=1421702&r1=1421701&r2=1421702&view=diff ============================================================================== --- incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/cluster/beans/ServerDTO.java (original) +++ incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/cluster/beans/ServerDTO.java Fri Dec 14 07:41:54 2012 @@ -78,5 +78,10 @@ public class ServerDTO { public void setWebapp(String webapp) { this.webapp = webapp; } + + @Override + public String toString() { + return "id "+id+" address "+address+" port "+port+" protocol "+protocol; + } } Modified: incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/cluster/sync/RestClient.java URL: http://svn.apache.org/viewvc/incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/cluster/sync/RestClient.java?rev=1421702&r1=1421701&r2=1421702&view=diff ============================================================================== --- incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/cluster/sync/RestClient.java (original) +++ incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/cluster/sync/RestClient.java Fri Dec 14 07:41:54 2012 @@ -37,6 +37,7 @@ import org.apache.axis2.transport.http.H import org.apache.openmeetings.OpenmeetingsVariables; import org.apache.openmeetings.conference.room.RoomClient; import org.apache.openmeetings.conference.room.SlaveClientDto; +import org.apache.openmeetings.documents.beans.UploadCompleteMessage; import org.apache.openmeetings.persistence.beans.basic.Server; import org.red5.logging.Red5LoggerFactory; import org.slf4j.Logger; @@ -55,7 +56,12 @@ public class RestClient { RestClient.class, OpenmeetingsVariables.webAppRootKey); private enum Action { - PING, KICK_USER + //send a ping to the user + PING, + //kick the user from the server + KICK_USER, + //send a sync message to a client on that server + SYNC_MESSAGE } /** @@ -77,6 +83,15 @@ public class RestClient { private boolean pingRunning = false; private String publicSID; + + private UploadCompleteMessage uploadCompleteMessage; + + /** + * there are two publicSIDs, one for the kickUser REST call and one for the syncMessage call + * theoretically they could be performed at the same time but to different users, so we don't want + * to use the same variable for both + */ + private String publicSIDSync; private static String nameSpaceForSlaveDto = "http://room.conference.openmeetings.apache.org/xsd"; @@ -100,6 +115,11 @@ public class RestClient { return protocol + "://" + host + ":" + port + "/" + webapp + "/services/ServerService"; } + + private String getRoomServiceEndPoint() { + return protocol + "://" + host + ":" + port + "/" + webapp + + "/services/RoomService"; + } /** * The observerInstance will be notified whenever a ping was completed @@ -200,21 +220,8 @@ public class RestClient { */ public void loginUser(Action action) throws Exception { - Options options = new Options(); - options.setTo(new EndpointReference(getUserServiceEndPoint())); - options.setProperty(Constants.Configuration.ENABLE_REST, - Constants.VALUE_TRUE); - int timeOutInMilliSeconds = 2000; - // setting timeout to 2 second should be sufficient, if the server is - // not available within the 3 second interval you got a problem anyway - options.setTimeOutInMilliSeconds(timeOutInMilliSeconds); - options.setProperty(HTTPConstants.SO_TIMEOUT, timeOutInMilliSeconds); - options.setProperty(HTTPConstants.CONNECTION_TIMEOUT, timeOutInMilliSeconds); - - ServiceClient sender = new ServiceClient(); - sender.engageModule(new QName(Constants.MODULE_ADDRESSING) - .getLocalPart()); - sender.setOptions(options); + ServiceClient sender = createServiceClient(getUserServiceEndPoint()); + OMElement getSessionResult = sender .sendReceive(getPayloadMethodGetSession()); sessionId = getSessionIdFromResult(getSessionResult); @@ -228,12 +235,117 @@ public class RestClient { ping(); } else if (action == Action.KICK_USER) { kickUserInternl(); + } else if (action == Action.SYNC_MESSAGE) { + syncMessageInternl(); + } + + } + + private ServiceClient createServiceClient(String serviceEndPoint) throws Exception { + ServiceClient sender = new ServiceClient(); + sender.engageModule(new QName(Constants.MODULE_ADDRESSING) + .getLocalPart()); + Options options = new Options(); + options.setTo(new EndpointReference(serviceEndPoint)); + options.setProperty(Constants.Configuration.ENABLE_REST, + Constants.VALUE_TRUE); + int timeOutInMilliSeconds = 2000; + // setting timeout to 2 second should be sufficient, if the server is + // not available within the 3 second interval you got a problem anyway + options.setTimeOutInMilliSeconds(timeOutInMilliSeconds); + options.setProperty(HTTPConstants.SO_TIMEOUT, timeOutInMilliSeconds); + options.setProperty(HTTPConstants.CONNECTION_TIMEOUT, timeOutInMilliSeconds); + sender.setOptions(options); + + return sender; + } + + private OMElement createOMElement(OMFactory fac, OMNamespace omNs, String name, String value) { + OMElement omElement = fac.createOMElement(name, omNs); + omElement.addChild(fac.createOMText(omElement, value)); + return omElement; + } + + + /** + * set s the publicSID the message object and sends it to the slave by calling a REST service + * + * @param publicSID + * @param uploadCompleteMessage + */ + public void syncMessage(String publicSID, UploadCompleteMessage uploadCompleteMessage) { + this.publicSIDSync = publicSID; + this.uploadCompleteMessage = uploadCompleteMessage; + syncMessageInternl(); + } + + private void syncMessageInternl() { + try { + + if (!loginSuccess) { + loginUser(Action.SYNC_MESSAGE); + } + + ServiceClient sender = createServiceClient(getRoomServiceEndPoint()); + OMElement syncMessageResult = sender + .sendReceive(getPayloadMethodSyncMessage()); + Boolean result = syncMessageResultFromResult(syncMessageResult); + + if (!result) { + throw new Exception("Could not sync message to slave host"); + } + + } catch (Exception err) { + log.error("[syncMessage failed]", err); } + } + + private Boolean syncMessageResultFromResult(OMElement result) throws Exception { + QName kickUserResult = new QName(NAMESPACE_PREFIX, "return"); + @SuppressWarnings("unchecked") + Iterator<OMElement> elements = result.getChildrenWithName(kickUserResult); + if (elements.hasNext()) { + OMElement resultElement = elements.next(); + if (resultElement.getText().equals("true")) { + return true; + } else { + throw new Exception("Could not delete user from slave host, returns: " + + resultElement.getText()); + } + } else { + throw new Exception("Could not parse kickUserByPublicSID result"); + } } + private OMElement getPayloadMethodSyncMessage() { + + OMFactory fac = OMAbstractFactory.getOMFactory(); + OMNamespace omNs = fac.createOMNamespace(NAMESPACE_PREFIX, "pre"); + OMElement method = fac.createOMElement("syncUploadCompleteMessage", omNs); + + method.addChild(createOMElement(fac, omNs, "SID", sessionId)); + method.addChild(createOMElement(fac, omNs, "publicSID", publicSIDSync)); + method.addChild(createOMElement(fac, omNs, "userId", ""+ uploadCompleteMessage.getUserId())); + method.addChild(createOMElement(fac, omNs, "message", uploadCompleteMessage.getMessage())); + method.addChild(createOMElement(fac, omNs, "action", uploadCompleteMessage.getAction())); + method.addChild(createOMElement(fac, omNs, "error", uploadCompleteMessage.getError())); + method.addChild(createOMElement(fac, omNs, "hasError", ""+uploadCompleteMessage.isHasError())); + method.addChild(createOMElement(fac, omNs, "fileName", uploadCompleteMessage.getFileName())); + + method.addChild(createOMElement(fac, omNs, "fileSystemName", uploadCompleteMessage.getFileSystemName())); + method.addChild(createOMElement(fac, omNs, "isPresentation", ""+uploadCompleteMessage.getIsPresentation())); + method.addChild(createOMElement(fac, omNs, "isImage", ""+uploadCompleteMessage.getIsImage())); + method.addChild(createOMElement(fac, omNs, "isVideo", ""+uploadCompleteMessage.getIsVideo())); + method.addChild(createOMElement(fac, omNs, "fileHash", uploadCompleteMessage.getFileHash())); + + return method; + } + /** * sets the publicSID and removes a user from a slave host by calling a REST service + * + * @param publicSID */ public void kickUser(String publicSID) { this.publicSID = publicSID; @@ -246,22 +358,9 @@ public class RestClient { if (!loginSuccess) { loginUser(Action.KICK_USER); } + + ServiceClient sender = createServiceClient(getUserServiceEndPoint()); - Options options = new Options(); - options.setTo(new EndpointReference(getUserServiceEndPoint())); - options.setProperty(Constants.Configuration.ENABLE_REST, - Constants.VALUE_TRUE); - int timeOutInMilliSeconds = 2000; - // setting timeout to 2 second should be sufficient, if the server is - // not available within the 3 second interval you got a problem anyway - options.setTimeOutInMilliSeconds(timeOutInMilliSeconds); - options.setProperty(HTTPConstants.SO_TIMEOUT, timeOutInMilliSeconds); - options.setProperty(HTTPConstants.CONNECTION_TIMEOUT, timeOutInMilliSeconds); - - ServiceClient sender = new ServiceClient(); - sender.engageModule(new QName(Constants.MODULE_ADDRESSING) - .getLocalPart()); - sender.setOptions(options); OMElement kickUserByPublicSIDResult = sender .sendReceive(getPayloadMethodKickUserByPublicSID()); Boolean result = kickUserByPublicSIDFromResult(kickUserByPublicSIDResult); @@ -297,15 +396,8 @@ public class RestClient { OMFactory fac = OMAbstractFactory.getOMFactory(); OMNamespace omNs = fac.createOMNamespace(NAMESPACE_PREFIX, "pre"); OMElement method = fac.createOMElement("kickUserByPublicSID", omNs); - - OMElement sid = fac.createOMElement("SID", omNs); - sid.addChild(fac.createOMText(sid, sessionId)); - method.addChild(sid); - - OMElement publicSIDOmElement = fac.createOMElement("publicSID", omNs); - publicSIDOmElement.addChild(fac.createOMText(publicSIDOmElement, publicSID)); - method.addChild(publicSIDOmElement); - + method.addChild(createOMElement(fac, omNs, "SID", sessionId)); + method.addChild(createOMElement(fac, omNs, "publicSID", publicSID)); return method; } @@ -326,22 +418,7 @@ public class RestClient { loginUser(Action.PING); } else { - Options options = new Options(); - options.setTo(new EndpointReference(getServerServiceEndPoint())); - options.setProperty(Constants.Configuration.ENABLE_REST, - Constants.VALUE_TRUE); - int timeOutInMilliSeconds = 2000; - // setting timeout to 2 second should be sufficient, if the server is - // not available within the 3 second interval you got a problem anyway - options.setTimeOutInMilliSeconds(timeOutInMilliSeconds); - options.setProperty(HTTPConstants.SO_TIMEOUT, timeOutInMilliSeconds); - options.setProperty(HTTPConstants.CONNECTION_TIMEOUT, timeOutInMilliSeconds); - - ServiceClient sender = new ServiceClient(); - sender.engageModule(new QName(Constants.MODULE_ADDRESSING) - .getLocalPart()); - sender.setOptions(options); - + ServiceClient sender = createServiceClient(getServerServiceEndPoint()); OMElement pingResult = sender .sendReceive(getPayloadMethodPingTemp()); @@ -412,19 +489,9 @@ public class RestClient { OMFactory fac = OMAbstractFactory.getOMFactory(); OMNamespace omNs = fac.createOMNamespace(NAMESPACE_PREFIX, "pre"); OMElement method = fac.createOMElement("loginUser", omNs); - - OMElement sid = fac.createOMElement("SID", omNs); - sid.addChild(fac.createOMText(sid, sessionId)); - method.addChild(sid); - - OMElement username = fac.createOMElement("username", omNs); - username.addChild(fac.createOMText(username, user)); - method.addChild(username); - - OMElement userpass = fac.createOMElement("userpass", omNs); - userpass.addChild(fac.createOMText(userpass, pass)); - method.addChild(userpass); - + method.addChild(createOMElement(fac, omNs, "SID", sessionId)); + method.addChild(createOMElement(fac, omNs, "username", user)); + method.addChild(createOMElement(fac, omNs, "userpass", pass)); return method; } @@ -464,11 +531,7 @@ public class RestClient { OMFactory fac = OMAbstractFactory.getOMFactory(); OMNamespace omNs = fac.createOMNamespace(NAMESPACE_PREFIX, "pre"); OMElement method = fac.createOMElement("ping", omNs); - - OMElement sid = fac.createOMElement("SID", omNs); - sid.addChild(fac.createOMText(sid, sessionId)); - method.addChild(sid); - + method.addChild(createOMElement(fac, omNs, "SID", sessionId)); return method; } @@ -541,5 +604,6 @@ public class RestClient { } return null; } - + + } Modified: incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/documents/beans/UploadCompleteMessage.java URL: http://svn.apache.org/viewvc/incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/documents/beans/UploadCompleteMessage.java?rev=1421702&r1=1421701&r2=1421702&view=diff ============================================================================== --- incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/documents/beans/UploadCompleteMessage.java (original) +++ incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/documents/beans/UploadCompleteMessage.java Fri Dec 14 07:41:54 2012 @@ -19,78 +19,162 @@ package org.apache.openmeetings.documents.beans; import org.apache.openmeetings.persistence.beans.files.FileExplorerItem; -import org.apache.openmeetings.persistence.beans.user.Users; + /** - * Helper bean that is send to client(s) once the servlet has completed the upload + * Helper bean that is send to client(s) once the servlet has completed the + * upload * * @author sebawagner - * + * */ public class UploadCompleteMessage { - - private Users user; + + private Long userId; private String message; private String action; private String error; private boolean hasError = false; private String fileName; - private FileExplorerItem fileExplorerItem; - + + // Properties from the file explorerItem + private String fileSystemName; + private Boolean isPresentation = false; + private Boolean isImage = false; + private Boolean isVideo = false; + private String fileHash; + public UploadCompleteMessage() { } - - public UploadCompleteMessage(Users user, String message, String action, + + public UploadCompleteMessage(Long userId, String message, String action, String error, String fileName) { super(); - this.user = user; + this.userId = userId; this.message = message; this.action = action; this.error = error; this.fileName = fileName; } - public Users getUser() { - return user; + public UploadCompleteMessage(Long userId, String message, String action, + String error, boolean hasError, String fileName, + String fileSystemName, boolean isPresentation, boolean isImage, + boolean isVideo, String fileHash) { + super(); + this.userId = userId; + this.message = message; + this.action = action; + this.error = error; + this.hasError = hasError; + this.fileName = fileName; + this.fileSystemName = fileSystemName; + this.isPresentation = isPresentation; + this.isImage = isImage; + this.isVideo = isVideo; + this.fileHash = fileHash; } - public void setUser(Users user) { - this.user = user; + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; } + public String getMessage() { return message; } + public void setMessage(String message) { this.message = message; } + public String getAction() { return action; } + public void setAction(String action) { this.action = action; } + public String getError() { return error; } + public void setError(String error) { this.error = error; } + public String getFileName() { return fileName; } + public void setFileName(String fileName) { this.fileName = fileName; } + public boolean isHasError() { return hasError; } + public void setHasError(boolean hasError) { this.hasError = hasError; } - public FileExplorerItem getFileExplorerItem() { - return fileExplorerItem; + + public String getFileSystemName() { + return fileSystemName; + } + + public void setFileSystemName(String fileSystemName) { + this.fileSystemName = fileSystemName; + } + + public Boolean getIsPresentation() { + return isPresentation; + } + + public void setIsPresentation(Boolean isPresentation) { + this.isPresentation = isPresentation; + } + + public Boolean getIsImage() { + return isImage; + } + + public void setIsImage(Boolean isImage) { + this.isImage = isImage; + } + + public Boolean getIsVideo() { + return isVideo; + } + + public void setIsVideo(Boolean isVideo) { + this.isVideo = isVideo; } + + public String getFileHash() { + return fileHash; + } + + public void setFileHash(String fileHash) { + this.fileHash = fileHash; + } + public void setFileExplorerItem(FileExplorerItem fileExplorerItem) { - this.fileExplorerItem = fileExplorerItem; + if (fileExplorerItem.getIsImage() != null) { + isImage = fileExplorerItem.getIsImage(); + } + if (fileExplorerItem.getIsVideo() != null) { + isVideo = fileExplorerItem.getIsVideo(); + } + if (fileExplorerItem.getIsPresentation() != null) { + isPresentation = fileExplorerItem.getIsPresentation(); + } + fileSystemName = fileExplorerItem.getFileName(); + fileHash = fileExplorerItem.getFileHash(); } } Modified: incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/quartz/scheduler/ClusterSlaveJob.java URL: http://svn.apache.org/viewvc/incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/quartz/scheduler/ClusterSlaveJob.java?rev=1421702&r1=1421701&r2=1421702&view=diff ============================================================================== --- incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/quartz/scheduler/ClusterSlaveJob.java (original) +++ incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/quartz/scheduler/ClusterSlaveJob.java Fri Dec 14 07:41:54 2012 @@ -29,6 +29,7 @@ import org.apache.openmeetings.cluster.s import org.apache.openmeetings.conference.room.ISharedSessionStore; import org.apache.openmeetings.conference.room.SlaveClientDto; import org.apache.openmeetings.data.basic.dao.ServerDao; +import org.apache.openmeetings.documents.beans.UploadCompleteMessage; import org.apache.openmeetings.persistence.beans.basic.Server; import org.red5.logging.Red5LoggerFactory; import org.slf4j.Logger; @@ -145,5 +146,28 @@ public class ClusterSlaveJob implements rClient.kickUser(publicSID); } + + /** + * Gets the current {@link RestClient} from the session store and then + * performs a kickUser on that. It is not possible that there is no + * {@link RestClient}, because if you want to kick a user from a slave, the + * master <i>must</i> already have loaded the sessions from the slave, so + * there logically <i>must</i> by a {@link RestClient} available that has an + * open connection to that slave / {@link Server} + * + * @param server + * @param publicSID + * @param uploadCompleteMessage + * @throws Exception + */ + public void syncMessageToClientOnSlave(Server server, String publicSID, UploadCompleteMessage uploadCompleteMessage) throws Exception { + RestClient rClient = getRestClient(server); + + if (rClient == null) { + throw new Exception("No RestClient found for server " + server); + } + + rClient.syncMessage(publicSID, uploadCompleteMessage); + } } Modified: incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/servlet/outputhandler/BackupImportController.java URL: http://svn.apache.org/viewvc/incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/servlet/outputhandler/BackupImportController.java?rev=1421702&r1=1421701&r2=1421702&view=diff ============================================================================== --- incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/servlet/outputhandler/BackupImportController.java (original) +++ incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/servlet/outputhandler/BackupImportController.java Fri Dec 14 07:41:54 2012 @@ -643,7 +643,7 @@ public class BackupImportController exte performImport(is); UploadCompleteMessage uploadCompleteMessage = new UploadCompleteMessage( - usersDao.get(info.userId), + info.userId, "library", //message "import", //action "", //error Modified: incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/servlet/outputhandler/ImportController.java URL: http://svn.apache.org/viewvc/incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/servlet/outputhandler/ImportController.java?rev=1421702&r1=1421701&r2=1421702&view=diff ============================================================================== --- incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/servlet/outputhandler/ImportController.java (original) +++ incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/servlet/outputhandler/ImportController.java Fri Dec 14 07:41:54 2012 @@ -89,7 +89,7 @@ public class ImportController extends Ab log.debug("moduleName.equals(userprofile) ! "); UploadCompleteMessage uploadCompleteMessage = new UploadCompleteMessage( - usersDao.get(info.userId), + info.userId, "library", //message "import", //action "", //error Modified: incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/servlet/outputhandler/UploadController.java URL: http://svn.apache.org/viewvc/incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/servlet/outputhandler/UploadController.java?rev=1421702&r1=1421701&r2=1421702&view=diff ============================================================================== --- incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/servlet/outputhandler/UploadController.java (original) +++ incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/servlet/outputhandler/UploadController.java Fri Dec 14 07:41:54 2012 @@ -108,12 +108,13 @@ public class UploadController extends Ab parentFolderId, info.filename, 0L, ""); // externalFilesId, externalType UploadCompleteMessage uploadCompleteMessage = new UploadCompleteMessage(); - uploadCompleteMessage.setUser(usersDao.get(info.userId)); + uploadCompleteMessage.setUserId(info.userId); // Flash cannot read the response of an upload // httpServletResponse.getWriter().print(returnError); uploadCompleteMessage.setMessage("library"); uploadCompleteMessage.setAction("newFile"); + uploadCompleteMessage.setFileExplorerItem( fileExplorerItemDao.getFileExplorerItemsById( returnError.getFileExplorerItemId())); @@ -162,7 +163,7 @@ public class UploadController extends Ab fileSystemName = StringUtils.deleteWhitespace(fileSystemName); UploadCompleteMessage uploadCompleteMessage = new UploadCompleteMessage(); - uploadCompleteMessage.setUser(usersDao.get(info.userId)); + uploadCompleteMessage.setUserId(info.userId); // Flash cannot read the response of an upload // httpServletResponse.getWriter().print(returnError); Added: incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/test/cluster/TestHashMapStoreSyncBug.java URL: http://svn.apache.org/viewvc/incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/test/cluster/TestHashMapStoreSyncBug.java?rev=1421702&view=auto ============================================================================== --- incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/test/cluster/TestHashMapStoreSyncBug.java (added) +++ incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/test/cluster/TestHashMapStoreSyncBug.java Fri Dec 14 07:41:54 2012 @@ -0,0 +1,189 @@ +package org.apache.openmeetings.test.cluster; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Random; + +import org.apache.openmeetings.OpenmeetingsVariables; +import org.apache.openmeetings.cluster.beans.ServerDTO; +import org.apache.openmeetings.conference.room.ClientListHashMapStore; +import org.apache.openmeetings.conference.room.RoomClient; +import org.apache.openmeetings.conference.room.SlaveClientDto; +import org.apache.openmeetings.conference.room.cache.HashMapStore; +import org.apache.openmeetings.data.basic.dao.ServerDao; +import org.apache.openmeetings.data.user.Usermanagement; +import org.apache.openmeetings.persistence.beans.basic.Server; +import org.apache.openmeetings.persistence.beans.basic.Sessiondata; +import org.apache.openmeetings.persistence.beans.user.Users; +import org.apache.openmeetings.remote.ConferenceService; +import org.apache.openmeetings.remote.MainService; +import org.apache.openmeetings.test.AbstractOpenmeetingsSpringTest; +import org.apache.openmeetings.utils.crypt.ICryptString; +import org.apache.openmeetings.utils.crypt.MD5Implementation; +import org.apache.openmeetings.utils.math.CalendarPatterns; +import org.junit.Test; +import org.red5.logging.Red5LoggerFactory; +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; + +public class TestHashMapStoreSyncBug extends AbstractOpenmeetingsSpringTest { + + protected static final Logger log = Red5LoggerFactory.getLogger( + TestHashMapStoreSyncBug.class, OpenmeetingsVariables.webAppRootKey); + + private ClientListHashMapStoreTesting sessionManager = new ClientListHashMapStoreTesting(); + + @Autowired + private MainService mService; + @Autowired + private Usermanagement userManagement; + @Autowired + private ConferenceService conferenceService; + @Autowired + private ServerDao serverDao; + + int localSessions = 200; + int slaveSessionSize = 200; + + @Test + public void doClientTest() { + + log.debug("Cache size " + sessionManager.getAllClients().size()); + + this.sessionManager.addClientListItem("streamId0", "hibernate", + 123, "localhost", "", false); + + RoomClient rcl = this.sessionManager.getClientByStreamId("streamId0", null); + rcl.setUser_id(Long.parseLong("1")); + rcl.setRoom_id(1L); + this.sessionManager.updateClientByStreamId("streamId0", rcl, false); + + Server s1 = serverDao.get(1L); + if (s1 == null) { + serverDao.saveServer(1L, "name 1", "127.0.0.1", 5080, "swagner", + "qweqwe", "openmeetings", "http", true, "", 1L); + s1 = serverDao.get(1L); + } + + List<SlaveClientDto> clients = new ArrayList<SlaveClientDto>(); + SlaveClientDto slaveDto = new SlaveClientDto( + // + "streamId0" , // + "publicSID_slave1", // + 2L, // + 2L, // + "firstName 2" , // + "lastName 2" , // + false, // + "2", // + "username 2" , // + CalendarPatterns + .getDateWithTimeByMiliSeconds(new Date())); // + clients.add(slaveDto); + + this.sessionManager.syncSlaveClientSession(s1, clients); + + sessionManager.getCache().printDebugInformation( + Arrays.asList(HashMapStore.DEBUG_DETAILS.SIZE, + HashMapStore.DEBUG_DETAILS.CLIENT_BY_STREAMID, + HashMapStore.DEBUG_DETAILS.CLIENT_BY_PUBLICSID, + HashMapStore.DEBUG_DETAILS.CLIENT_BY_USERID, + HashMapStore.DEBUG_DETAILS.CLIENT_BY_ROOMID)); + + Sessiondata sessionData = mService.getsessiondata(); + + Users us = (Users) userManagement.loginUser( + sessionData.getSession_id(), username, userpass, null, false); + + log.debug("us " + us); + + assertTrue(us != null); + + // Is running already on server null + ServerDTO server = conferenceService.getServerForSession( + sessionData.getSession_id(), 1); + log.debug("server " + server); + assertTrue(server == null); + + // Is running already on server 1 + ServerDTO servert2 = conferenceService.getServerForSession( + sessionData.getSession_id(), 2); + log.debug("servert2 " + servert2); + assertEquals(servert2.getId().longValue(), 1); + + //empty server 1 + List<SlaveClientDto> clientsServer1 = new ArrayList<SlaveClientDto>(); + this.sessionManager.syncSlaveClientSession(s1, clientsServer1); + + log.debug("\n\r##################### \n\r AFTER USER IS REMOVED \n\r####################"); + + sessionManager.getCache().printDebugInformation( + Arrays.asList(HashMapStore.DEBUG_DETAILS.SIZE, + HashMapStore.DEBUG_DETAILS.CLIENT_BY_STREAMID, + HashMapStore.DEBUG_DETAILS.CLIENT_BY_PUBLICSID, + HashMapStore.DEBUG_DETAILS.CLIENT_BY_USERID, + HashMapStore.DEBUG_DETAILS.CLIENT_BY_ROOMID)); + + // Is running already on server null + ServerDTO servert3 = conferenceService.getServerForSession( + sessionData.getSession_id(), 1); + log.debug("servert3 " + servert3); + assertTrue(servert3 == null); + + } + + private class ClientListHashMapStoreTesting extends ClientListHashMapStore { + + public synchronized RoomClient addClientListItem(String streamId, + String scopeName, Integer remotePort, String remoteAddress, + String swfUrl, boolean isAVClient) { + try { + + // Store the Connection into a bean and add it to the HashMap + RoomClient rcm = new RoomClient(); + rcm.setConnectedSince(new Date()); + rcm.setStreamid(streamId); + rcm.setScope(scopeName); + + long random = System.currentTimeMillis() + + new BigInteger(256, new Random()).longValue(); + + ICryptString cryptStyle = new MD5Implementation(); + + rcm.setPublicSID(cryptStyle.createPassPhrase(String.valueOf( + random).toString())); + + rcm.setUserport(remotePort); + rcm.setUserip(remoteAddress); + rcm.setSwfurl(swfUrl); + rcm.setIsMod(new Boolean(false)); + rcm.setCanDraw(new Boolean(false)); + rcm.setIsAVClient(isAVClient); + + if (cache.containsKey(null, streamId)) { + log.error("Tried to add an existing Client " + streamId); + return null; + } + + cache.put(null, rcm.getStreamid(), rcm); + + return rcm; + } catch (Exception err) { + log.error("[addClientListItem]", err); + } + return null; + } + + public HashMapStore getCache() { + return cache; + } + + } + +}