From d0b7054b77be483faa5b9152b1601367281720c0 Mon Sep 17 00:00:00 2001 From: Sean Flanigan Date: Mon, 13 Aug 2012 18:03:16 +1000 Subject: [PATCH] rhbz833639 Remove editor clients on GWTEventService timeout --- .../SequentialConnectionIdGenerator.java | 56 +++++++++++++++++++ .../zanata/webtrans/client/Application.java | 19 ++++++- .../webtrans/client/EventProcessor.java | 7 ++- .../webtrans/server/TranslationWorkspace.java | 1 + .../server/TranslationWorkspaceImpl.java | 31 +++++++++- .../rpc/EventServiceConnectedHandler.java | 39 +++++++++++++ .../server/rpc/ExitWorkspaceHandler.java | 10 +--- .../rpc/EventServiceConnectedAction.java | 25 +++++++++ .../shared/rpc/ExitWorkspaceResult.java | 1 + .../rpc/PublishWorkspaceChatResult.java | 1 + .../shared/rpc/TransUnitEditResult.java | 1 + .../main/resources/eventservice.properties | 3 +- 12 files changed, 175 insertions(+), 19 deletions(-) create mode 100644 zanata-war/src/main/java/org/zanata/eventservice/SequentialConnectionIdGenerator.java create mode 100644 zanata-war/src/main/java/org/zanata/webtrans/server/rpc/EventServiceConnectedHandler.java create mode 100644 zanata-war/src/main/java/org/zanata/webtrans/shared/rpc/EventServiceConnectedAction.java diff --git a/zanata-war/src/main/java/org/zanata/eventservice/SequentialConnectionIdGenerator.java b/zanata-war/src/main/java/org/zanata/eventservice/SequentialConnectionIdGenerator.java new file mode 100644 index 0000000000..9b1f4fafd8 --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/eventservice/SequentialConnectionIdGenerator.java @@ -0,0 +1,56 @@ +/* + * Copyright 2012, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.zanata.eventservice; + +import javax.servlet.http.HttpServletRequest; + +import lombok.Synchronized; + +import de.novanic.eventservice.service.connection.id.ConnectionIdGenerator; + +/** + * @author Sean Flanigan sflaniga@redhat.com + * + */ +public class SequentialConnectionIdGenerator implements ConnectionIdGenerator +{ + private static long nextConnectionNum = 0; + + @Synchronized + private static long generateConnectionNum() + { + return nextConnectionNum++; + } + + @Override + public String generateConnectionId(HttpServletRequest aRequest) + { + return String.valueOf(aRequest.getSession(true).getId() + "-" + generateConnectionNum()); + } + + @Override + public String getConnectionId(HttpServletRequest aRequest) + { + return aRequest.getParameter("id"); + } + +} diff --git a/zanata-war/src/main/java/org/zanata/webtrans/client/Application.java b/zanata-war/src/main/java/org/zanata/webtrans/client/Application.java index 88696b78cb..0f3328bc10 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/client/Application.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/client/Application.java @@ -10,10 +10,12 @@ import org.zanata.webtrans.shared.model.ProjectIterationId; import org.zanata.webtrans.shared.model.UserWorkspaceContext; import org.zanata.webtrans.shared.model.WorkspaceId; +import org.zanata.webtrans.shared.rpc.EventServiceConnectedAction; import org.zanata.webtrans.shared.rpc.ActivateWorkspaceAction; import org.zanata.webtrans.shared.rpc.ActivateWorkspaceResult; import org.zanata.webtrans.shared.rpc.ExitWorkspaceAction; import org.zanata.webtrans.shared.rpc.ExitWorkspaceResult; +import org.zanata.webtrans.shared.rpc.NoOpResult; import com.allen_sauer.gwt.log.client.Log; import com.google.common.base.Strings; @@ -127,9 +129,22 @@ public void onWindowClosing(ClosingEvent event) eventProcessor.start(new StartCallback() { @Override - public void onSuccess() + public void onSuccess(String connectionId) { - delayedStartApp(); + // tell server the ConnectionId for this EditorClientId + injector.getDispatcher().execute(new EventServiceConnectedAction(connectionId), new AsyncCallback() + { + @Override + public void onFailure(Throwable e) + { + RootLayoutPanel.get().add(new HTML("

Server communication failed...

" + "Exception: " + e.getMessage())); + } + @Override + public void onSuccess(NoOpResult result) + { + delayedStartApp(); + } + }); } @Override diff --git a/zanata-war/src/main/java/org/zanata/webtrans/client/EventProcessor.java b/zanata-war/src/main/java/org/zanata/webtrans/client/EventProcessor.java index 5712af6745..797e23e75c 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/client/EventProcessor.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/client/EventProcessor.java @@ -32,6 +32,7 @@ import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.inject.Inject; +import de.novanic.eventservice.client.config.ConfigurationTransferableDependentFactory; import de.novanic.eventservice.client.event.Event; import de.novanic.eventservice.client.event.RemoteEventService; import de.novanic.eventservice.client.event.RemoteEventServiceFactory; @@ -49,8 +50,7 @@ private interface EventFactory> public static interface StartCallback { - void onSuccess(); - + void onSuccess(String connectionId); void onFailure(Throwable e); } @@ -153,7 +153,8 @@ public void start(final StartCallback callback) public void onSuccess(Void result) { Log.info("EventProcessor is now listening for events in the domain " + domain.getName()); - callback.onSuccess(); + String connectionId = ConfigurationTransferableDependentFactory.getConfiguration().getConnectionId(); + callback.onSuccess(connectionId); } @Override diff --git a/zanata-war/src/main/java/org/zanata/webtrans/server/TranslationWorkspace.java b/zanata-war/src/main/java/org/zanata/webtrans/server/TranslationWorkspace.java index 06b4ee0e0f..0659de7639 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/server/TranslationWorkspace.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/server/TranslationWorkspace.java @@ -46,4 +46,5 @@ public interface TranslationWorkspace WorkspaceContext getWorkspaceContext(); void updateUserSelection(EditorClientId editorClientId, TransUnit selectedTransUnit); TransUnit getUserSelection(EditorClientId editorClientId); + public void onEventServiceConnected(EditorClientId editorClientId, String connectionId); } \ No newline at end of file diff --git a/zanata-war/src/main/java/org/zanata/webtrans/server/TranslationWorkspaceImpl.java b/zanata-war/src/main/java/org/zanata/webtrans/server/TranslationWorkspaceImpl.java index 03ee84ff3d..38ad646448 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/server/TranslationWorkspaceImpl.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/server/TranslationWorkspaceImpl.java @@ -1,6 +1,7 @@ package org.zanata.webtrans.server; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentMap; @@ -14,11 +15,13 @@ import org.zanata.webtrans.shared.model.TransUnit; import org.zanata.webtrans.shared.model.TransUnitId; import org.zanata.webtrans.shared.model.WorkspaceContext; +import org.zanata.webtrans.shared.rpc.ExitWorkspace; import org.zanata.webtrans.shared.rpc.SessionEventData; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.MapMaker; +import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; @@ -38,6 +41,7 @@ public class TranslationWorkspaceImpl implements TranslationWorkspace private final Domain domain; private final ConcurrentMap sessions = new MapMaker().makeMap(); private final Multimap httpSessionToEditorClientId; + private final Map connectionIdToEditorClientId; private final ConcurrentMap editstatus = new MapMaker().makeMap(); private final EventExecutorService eventExecutorService; @@ -45,6 +49,8 @@ public class TranslationWorkspaceImpl implements TranslationWorkspace { ArrayListMultimap almm = ArrayListMultimap.create(); httpSessionToEditorClientId = Multimaps.synchronizedListMultimap(almm); + Map connMap = Maps.newHashMap(); + connectionIdToEditorClientId = Collections.synchronizedMap(connMap); } public TranslationWorkspaceImpl(WorkspaceContext workspaceContext) @@ -64,9 +70,16 @@ public TranslationWorkspaceImpl(WorkspaceContext workspaceContext) public void onTimeout(UserInfo userInfo) { String connectionId = userInfo.getUserId(); - log.info("Timeout for GWTEventService connectionId {0}", connectionId); - // TODO look up EditorClientId for connectionId - // removeEditorClient(editorClientId); + EditorClientId editorClientId = connectionIdToEditorClientId.remove(connectionId); + if (editorClientId != null) + { + log.info("Timeout for GWTEventService connectionId {0}; removing EditorClientId {1}", connectionId, editorClientId); + removeEditorClient(editorClientId); + } + else + { + log.info("Timeout for GWTEventService connectionId {0}; nothing to do", connectionId); + } } }); } @@ -136,6 +149,13 @@ public void addEditorClient(String httpSessionId, EditorClientId editorClientId, httpSessionToEditorClientId.put(httpSessionId, editorClientId); } + @Override + public void onEventServiceConnected(EditorClientId editorClientId, String connectionId) + { + log.info("EditorClientId {0} has connectionId {1}", editorClientId, connectionId); + connectionIdToEditorClientId.put(connectionId, editorClientId); + } + @Override public Collection removeEditorClients(String httpSessionId) { @@ -160,6 +180,11 @@ public boolean removeEditorClient(EditorClientId editorClientId) if (clientIds != null) { clientIds.remove(editorClientId); + + // Send GWT Event to clients to update the user list + ExitWorkspace event = new ExitWorkspace(editorClientId, details.getPerson()); + publish(event); + log.info("Removed user {0} with editorClientId {1} from workspace {2}", details.getPerson().getId(), editorClientId, workspaceContext); return true; } diff --git a/zanata-war/src/main/java/org/zanata/webtrans/server/rpc/EventServiceConnectedHandler.java b/zanata-war/src/main/java/org/zanata/webtrans/server/rpc/EventServiceConnectedHandler.java new file mode 100644 index 0000000000..8365766213 --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/webtrans/server/rpc/EventServiceConnectedHandler.java @@ -0,0 +1,39 @@ +package org.zanata.webtrans.server.rpc; + +import net.customware.gwt.dispatch.server.ExecutionContext; +import net.customware.gwt.dispatch.shared.ActionException; + +import org.jboss.seam.ScopeType; +import org.jboss.seam.annotations.In; +import org.jboss.seam.annotations.Name; +import org.jboss.seam.annotations.Scope; +import org.zanata.security.ZanataIdentity; +import org.zanata.webtrans.server.ActionHandlerFor; +import org.zanata.webtrans.server.TranslationWorkspace; +import org.zanata.webtrans.server.TranslationWorkspaceManager; +import org.zanata.webtrans.shared.rpc.EventServiceConnectedAction; +import org.zanata.webtrans.shared.rpc.NoOpResult; + +@Name("webtrans.gwt.ActivateEventServiceHandler") +@Scope(ScopeType.STATELESS) +@ActionHandlerFor(EventServiceConnectedAction.class) +public class EventServiceConnectedHandler extends AbstractActionHandler +{ + + @In + TranslationWorkspaceManager translationWorkspaceManager; + + @Override + public NoOpResult execute(EventServiceConnectedAction action, ExecutionContext context) throws ActionException + { + ZanataIdentity.instance().checkLoggedIn(); + TranslationWorkspace workspace = translationWorkspaceManager.getOrRegisterWorkspace(action.getWorkspaceId()); + workspace.onEventServiceConnected(action.getEditorClientId(), action.getConnectionId()); + return new NoOpResult(); + } + + @Override + public void rollback(EventServiceConnectedAction action, NoOpResult result, ExecutionContext context) throws ActionException + { + } +} diff --git a/zanata-war/src/main/java/org/zanata/webtrans/server/rpc/ExitWorkspaceHandler.java b/zanata-war/src/main/java/org/zanata/webtrans/server/rpc/ExitWorkspaceHandler.java index 5468ca72ca..7ee4c98397 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/server/rpc/ExitWorkspaceHandler.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/server/rpc/ExitWorkspaceHandler.java @@ -11,7 +11,6 @@ import org.zanata.webtrans.server.ActionHandlerFor; import org.zanata.webtrans.server.TranslationWorkspace; import org.zanata.webtrans.server.TranslationWorkspaceManager; -import org.zanata.webtrans.shared.rpc.ExitWorkspace; import org.zanata.webtrans.shared.rpc.ExitWorkspaceAction; import org.zanata.webtrans.shared.rpc.ExitWorkspaceResult; @@ -33,14 +32,7 @@ public ExitWorkspaceResult execute(ExitWorkspaceAction action, ExecutionContext TranslationWorkspace workspace = translationWorkspaceManager.getOrRegisterWorkspace(action.getWorkspaceId()); // Send ExitWorkspace event to client - boolean isRemoved = workspace.removeEditorClient(action.getEditorClientId()); - if (isRemoved) - { - // Send GWT Event to client to update the userlist - ExitWorkspace event = new ExitWorkspace(action.getEditorClientId(), action.getPerson()); - workspace.publish(event); - } - + workspace.removeEditorClient(action.getEditorClientId()); return new ExitWorkspaceResult(action.getPerson().getId().toString()); } diff --git a/zanata-war/src/main/java/org/zanata/webtrans/shared/rpc/EventServiceConnectedAction.java b/zanata-war/src/main/java/org/zanata/webtrans/shared/rpc/EventServiceConnectedAction.java new file mode 100644 index 0000000000..59d73b75f9 --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/webtrans/shared/rpc/EventServiceConnectedAction.java @@ -0,0 +1,25 @@ +package org.zanata.webtrans.shared.rpc; + + +public class EventServiceConnectedAction extends AbstractWorkspaceAction +{ + + private static final long serialVersionUID = 1L; + private String connectionId; + + @SuppressWarnings("unused") + private EventServiceConnectedAction() + { + } + + public EventServiceConnectedAction(String connectionId) + { + this.connectionId = connectionId; + } + + public String getConnectionId() + { + return connectionId; + } + +} diff --git a/zanata-war/src/main/java/org/zanata/webtrans/shared/rpc/ExitWorkspaceResult.java b/zanata-war/src/main/java/org/zanata/webtrans/shared/rpc/ExitWorkspaceResult.java index 01053cc414..448b3273f0 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/shared/rpc/ExitWorkspaceResult.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/shared/rpc/ExitWorkspaceResult.java @@ -1,6 +1,7 @@ package org.zanata.webtrans.shared.rpc; +// TODO replace this class with NoOpResult public class ExitWorkspaceResult implements DispatchResult { diff --git a/zanata-war/src/main/java/org/zanata/webtrans/shared/rpc/PublishWorkspaceChatResult.java b/zanata-war/src/main/java/org/zanata/webtrans/shared/rpc/PublishWorkspaceChatResult.java index 7e45e178c8..3c138aa61a 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/shared/rpc/PublishWorkspaceChatResult.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/shared/rpc/PublishWorkspaceChatResult.java @@ -1,6 +1,7 @@ package org.zanata.webtrans.shared.rpc; +//TODO replace this class with NoOpResult public class PublishWorkspaceChatResult implements DispatchResult { private static final long serialVersionUID = -611647552706494146L; diff --git a/zanata-war/src/main/java/org/zanata/webtrans/shared/rpc/TransUnitEditResult.java b/zanata-war/src/main/java/org/zanata/webtrans/shared/rpc/TransUnitEditResult.java index a4e0c31ab2..1178b95f05 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/shared/rpc/TransUnitEditResult.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/shared/rpc/TransUnitEditResult.java @@ -1,6 +1,7 @@ package org.zanata.webtrans.shared.rpc; +//TODO replace this class with NoOpResult public class TransUnitEditResult implements DispatchResult { private static final long serialVersionUID = -950617414456103445L; diff --git a/zanata-war/src/main/resources/eventservice.properties b/zanata-war/src/main/resources/eventservice.properties index 6a7367227f..1be68ad2a7 100644 --- a/zanata-war/src/main/resources/eventservice.properties +++ b/zanata-war/src/main/resources/eventservice.properties @@ -10,8 +10,7 @@ time.waiting.min=0 time.timeout=90000 # Generator for unique client ids -#eventservice.connection.id.generator=de.novanic.eventservice.service.connection.id.SessionConnectionIdGenerator -eventservice.connection.id.generator=de.novanic.eventservice.service.connection.id.SessionExtendedConnectionIdGenerator +eventservice.connection.id.generator=org.zanata.eventservice.SequentialConnectionIdGenerator # Connection strategy (server side part / connector) to define the transfer of events between client and server side eventservice.connection.strategy.server.connector=de.novanic.eventservice.service.connection.strategy.connector.longpolling.LongPollingServerConnector